Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Refactor QueryTable to use react-table #11216

Merged
merged 10 commits into from
Oct 19, 2020
18 changes: 11 additions & 7 deletions superset-frontend/spec/javascripts/sqllab/QueryTable_spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { Table } from 'reactable-arc';
import QueryTable from 'src/SqlLab/components/QueryTable';

import TableView from 'src/components/TableView';
import { TableCollection } from 'src/components/dataViewCommon';
import { queries } from './fixtures';

describe('QueryTable', () => {
Expand All @@ -35,10 +35,14 @@ describe('QueryTable', () => {
});
it('renders a proper table', () => {
const wrapper = shallow(<QueryTable {...mockedProps} />);
expect(wrapper.find(Table)).toExist();
expect(wrapper.find(Table).shallow().find('table')).toExist();
expect(wrapper.find(Table).shallow().find('table').find('Tr')).toHaveLength(
2,
);
const tableWrapper = wrapper
.find(TableView)
.shallow()
.find(TableCollection)
.shallow();
expect(wrapper.find(TableView)).toExist();
expect(tableWrapper.find('table')).toExist();
expect(tableWrapper.find('table').find('thead').find('tr')).toHaveLength(1);
expect(tableWrapper.find('table').find('tbody').find('tr')).toHaveLength(2);
});
});
17 changes: 13 additions & 4 deletions superset-frontend/src/SqlLab/components/QuerySearch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Button from 'src/components/Button';
import Select from 'src/components/Select';
import { t, SupersetClient } from '@superset-ui/core';
import { styled, t, SupersetClient } from '@superset-ui/core';

import Loading from '../../components/Loading';
import QueryTable from './QueryTable';
Expand All @@ -39,6 +39,15 @@ const propTypes = {
displayLimit: PropTypes.number.isRequired,
};

const TableStyles = styled.div`
height: ${props => props.height};

.table > thead > tr > th {
border-bottom: 2px solid #cfd8dc;
background: #f5f5f5;
}
`;

class QuerySearch extends React.PureComponent {
constructor(props) {
super(props);
Expand Down Expand Up @@ -278,9 +287,9 @@ class QuerySearch extends React.PureComponent {
{this.state.queriesLoading ? (
<Loading />
) : (
<div
<TableStyles
className="scrollbar-content"
style={{ height: this.props.height }}
height={this.props.height}
>
<QueryTable
columns={[
Expand All @@ -299,7 +308,7 @@ class QuerySearch extends React.PureComponent {
actions={this.props.actions}
displayLimit={this.props.displayLimit}
/>
</div>
</TableStyles>
)}
</div>
</div>
Expand Down
286 changes: 149 additions & 137 deletions superset-frontend/src/SqlLab/components/QueryTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,18 @@
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { Table } from 'reactable-arc';
import { ProgressBar, Well } from 'react-bootstrap';
import Label from 'src/components/Label';
import { t } from '@superset-ui/core';
import memoize from 'lodash/memoize';

import TableView from 'src/components/TableView';
import Button from 'src/components/Button';
import { fDuration } from 'src/modules/dates';
import Link from '../../components/Link';
import ResultSet from './ResultSet';
import ModalTrigger from '../../components/ModalTrigger';
import HighlightedSql from './HighlightedSql';
import { fDuration } from '../../modules/dates';
import QueryStateLabel from './QueryStateLabel';

const propTypes = {
Expand Down Expand Up @@ -74,151 +75,162 @@ class QueryTable extends React.PureComponent {
}

render() {
const data = this.props.queries
.map(query => {
const q = { ...query };
if (q.endDttm) {
q.duration = fDuration(q.startDttm, q.endDttm);
}
const time = moment(q.startDttm).format().split('T');
q.time = (
<div>
<span>
{time[0]} <br /> {time[1]}
</span>
</div>
);
q.user = (
<Button
buttonSize="small"
buttonStyle="link"
onClick={this.props.onUserClicked.bind(this, q.userId)}
>
{q.user}
</Button>
);
q.db = (
<Button
buttonSize="small"
buttonStyle="link"
onClick={this.props.onDbClicked.bind(this, q.dbId)}
>
{q.db}
</Button>
);
q.started = moment(q.startDttm).format('HH:mm:ss');
q.querylink = (
<div style={{ width: '100px' }}>
const data = memoize(() =>
this.props.queries
.map(query => {
const q = { ...query };
if (q.endDttm) {
q.duration = fDuration(q.startDttm, q.endDttm);
}
const time = moment(q.startDttm).format().split('T');
q.time = (
<div>
<span>
{time[0]} <br /> {time[1]}
</span>
</div>
);
q.user = (
<Button
buttonSize="small"
buttonStyle="link"
onClick={this.openQuery.bind(this, q.queryId)}
onClick={this.props.onUserClicked.bind(this, q.userId)}
>
<i className="fa fa-external-link m-r-3" />
{t('Edit')}
{q.user}
</Button>
</div>
);
q.sql = (
<Well>
<HighlightedSql
sql={q.sql}
rawSql={q.executedSql}
shrink
maxWidth={60}
/>
</Well>
);
if (q.resultsKey) {
q.output = (
<ModalTrigger
bsSize="large"
className="ResultsModal"
triggerNode={
<Label bsStyle="info" className="pointer">
{t('view results')}
</Label>
}
modalTitle={t('Data preview')}
beforeOpen={this.openAsyncResults.bind(
this,
query,
this.props.displayLimit,
)}
onExit={this.clearQueryResults.bind(this, query)}
modalBody={
<ResultSet
showSql
query={query}
actions={this.props.actions}
height={400}
displayLimit={this.props.displayLimit}
/>
}
/>
);
} else {
// 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.progress = (
<ProgressBar
style={{ width: '75px' }}
striped
now={q.progress}
label={`${q.progress.toFixed(0)}%`}
/>
);
let errorTooltip;
if (q.errorMessage) {
errorTooltip = (
<Link tooltip={q.errorMessage}>
<i className="fa fa-exclamation-circle text-danger" />
</Link>
q.db = (
<Button
buttonSize="small"
buttonStyle="link"
onClick={this.props.onDbClicked.bind(this, q.dbId)}
>
{q.db}
</Button>
);
}
q.state = (
<div>
<QueryStateLabel query={query} />
{errorTooltip}
</div>
);
q.actions = (
<div style={{ width: '75px' }}>
<Link
className="fa fa-pencil m-r-3"
onClick={this.restoreSql.bind(this, query)}
tooltip={t(
'Overwrite text in the editor with a query on this table',
)}
placement="top"
/>
<Link
className="fa fa-plus-circle m-r-3"
onClick={this.openQueryInNewTab.bind(this, query)}
tooltip={t('Run query in a new tab')}
placement="top"
/>
<Link
className="fa fa-trash m-r-3"
tooltip={t('Remove query from log')}
onClick={this.removeQuery.bind(this, query)}
q.started = moment(q.startDttm).format('HH:mm:ss');
q.querylink = (
<div style={{ width: '100px' }}>
<Button
buttonSize="small"
buttonStyle="link"
onClick={this.openQuery.bind(this, q.queryId)}
>
<i className="fa fa-external-link m-r-3" />
{t('Edit')}
</Button>
</div>
);
q.sql = (
<Well>
<HighlightedSql
sql={q.sql}
rawSql={q.executedSql}
shrink
maxWidth={60}
/>
</Well>
);
if (q.resultsKey) {
q.output = (
<ModalTrigger
bsSize="large"
className="ResultsModal"
triggerNode={
<Label bsStyle="info" className="pointer">
{t('view results')}
</Label>
}
modalTitle={t('Data preview')}
beforeOpen={this.openAsyncResults.bind(
this,
query,
this.props.displayLimit,
)}
onExit={this.clearQueryResults.bind(this, query)}
modalBody={
<ResultSet
showSql
query={query}
actions={this.props.actions}
height={400}
displayLimit={this.props.displayLimit}
/>
}
/>
);
} else {
// 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.progress = (
<ProgressBar
style={{ width: '75px' }}
striped
now={q.progress}
label={`${q.progress.toFixed(0)}%`}
/>
</div>
);
return q;
})
.reverse();
);
let errorTooltip;
if (q.errorMessage) {
errorTooltip = (
<Link tooltip={q.errorMessage}>
<i className="fa fa-exclamation-circle text-danger" />
</Link>
);
}
q.state = (
<div>
<QueryStateLabel query={query} />
{errorTooltip}
</div>
);
q.actions = (
<div style={{ width: '75px' }}>
<Link
className="fa fa-pencil m-r-3"
onClick={this.restoreSql.bind(this, query)}
tooltip={t(
'Overwrite text in the editor with a query on this table',
)}
placement="top"
/>
<Link
className="fa fa-plus-circle m-r-3"
onClick={this.openQueryInNewTab.bind(this, query)}
tooltip={t('Run query in a new tab')}
placement="top"
/>
<Link
className="fa fa-trash m-r-3"
tooltip={t('Remove query from log')}
onClick={this.removeQuery.bind(this, query)}
/>
</div>
);
return q;
})
.reverse(),
);
const columns = memoize(() =>
this.props.columns.map(column => ({
accessor: column,
Header: column,
disableSortBy: true,
})),
);
return (
<div className="QueryTable">
<Table
columns={this.props.columns}
className="table table-condensed"
data={data}
itemsPerPage={50}
<TableView
columns={columns()}
data={data()}
className="table-condensed"
pageSize={50}
/>
</div>
);
Expand Down