Skip to content

Commit ab083b8

Browse files
authoredOct 29, 2016
[sqllab] slide animations when adding/removing/toggling TableElement (#1472)
* [sqllab] slide animations when adding/removing/toggling TableElement * Adressing comments
1 parent 4bf5252 commit ab083b8

File tree

6 files changed

+135
-103
lines changed

6 files changed

+135
-103
lines changed
 

‎caravel/assets/javascripts/SqlLab/components/CopyQueryTabUrl.jsx

+2-7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import CopyToClipboard from '../../components/CopyToClipboard';
33
import { getShortUrl } from '../../../utils/common';
44

55
const propTypes = {
6-
queryEditor: React.PropTypes.object,
7-
};
8-
9-
const defaultProps = {
10-
queryEditor: null,
6+
queryEditor: React.PropTypes.object.isRequired,
117
};
128

139
export default class CopyQueryTabUrl extends React.PureComponent {
@@ -19,8 +15,8 @@ export default class CopyQueryTabUrl extends React.PureComponent {
1915
}
2016

2117
componentWillMount() {
22-
const params = [];
2318
const qe = this.props.queryEditor;
19+
const params = [];
2420
if (qe.dbId) params.push('dbid=' + qe.dbId);
2521
if (qe.title) params.push('title=' + encodeURIComponent(qe.title));
2622
if (qe.schema) params.push('schema=' + encodeURIComponent(qe.schema));
@@ -52,4 +48,3 @@ export default class CopyQueryTabUrl extends React.PureComponent {
5248
}
5349

5450
CopyQueryTabUrl.propTypes = propTypes;
55-
CopyQueryTabUrl.defaultProps = defaultProps;

‎caravel/assets/javascripts/SqlLab/components/TabbedSqlEditors.jsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,11 @@ class TabbedSqlEditors extends React.PureComponent {
122122
<MenuItem eventKey="2" onClick={this.renameTab.bind(this, qe)}>
123123
<i className="fa fa-i-cursor" /> rename tab
124124
</MenuItem>
125-
<MenuItem eventKey="3">
126-
<i className="fa fa-clipboard" /> <CopyQueryTabUrl queryEditor={qe} />
127-
</MenuItem>
125+
{qe &&
126+
<MenuItem eventKey="3">
127+
<i className="fa fa-clipboard" /> <CopyQueryTabUrl queryEditor={qe} />
128+
</MenuItem>
129+
}
128130
</DropdownButton>
129131
</div>
130132
);

‎caravel/assets/javascripts/SqlLab/components/TableElement.jsx

+99-82
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
import { ButtonGroup, Well } from 'react-bootstrap';
3+
import { ButtonGroup, Collapse, Well } from 'react-bootstrap';
44
import shortid from 'shortid';
55

66
import CopyToClipboard from '../../components/CopyToClipboard';
@@ -10,18 +10,21 @@ import ModalTrigger from '../../components/ModalTrigger';
1010
const propTypes = {
1111
table: React.PropTypes.object,
1212
actions: React.PropTypes.object,
13+
timeout: React.PropTypes.integer, // used for tests
1314
};
1415

1516
const defaultProps = {
16-
table: null,
1717
actions: {},
18+
table: null,
19+
timeout: 500,
1820
};
1921

2022
class TableElement extends React.PureComponent {
2123
constructor(props) {
2224
super(props);
2325
this.state = {
2426
sortColumns: false,
27+
expanded: true,
2528
};
2629
}
2730

@@ -36,18 +39,17 @@ class TableElement extends React.PureComponent {
3639
this.props.actions.addQueryEditor(qe);
3740
}
3841

39-
collapseTable(e) {
40-
e.preventDefault();
41-
this.props.actions.collapseTable(this.props.table);
42-
}
43-
44-
expandTable(e) {
42+
toggleTable(e) {
4543
e.preventDefault();
46-
this.props.actions.expandTable(this.props.table);
44+
if (this.props.table.expanded) {
45+
this.props.actions.collapseTable(this.props.table);
46+
} else {
47+
this.props.actions.expandTable(this.props.table);
48+
}
4749
}
4850

4951
removeTable() {
50-
this.props.actions.removeTable(this.props.table);
52+
this.setState({ expanded: false });
5153
}
5254
dataPreviewModal() {
5355
const query = {
@@ -65,11 +67,8 @@ class TableElement extends React.PureComponent {
6567
this.setState({ sortColumns: !this.state.sortColumns });
6668
}
6769

68-
render() {
70+
renderHeader() {
6971
const table = this.props.table;
70-
let metadata = null;
71-
let buttonToggle;
72-
7372
let header;
7473
if (table.partitions) {
7574
let partitionQuery;
@@ -101,25 +100,26 @@ class TableElement extends React.PureComponent {
101100
</Well>
102101
);
103102
}
104-
if (table.expanded) {
105-
buttonToggle = (
106-
<a
107-
href="#"
108-
onClick={(e) => { this.collapseTable(e); }}
109-
>
110-
<strong>{table.name}</strong>
111-
<small className="m-l-5"><i className="fa fa-minus" /></small>
112-
</a>
113-
);
114-
const cols = table.columns.slice();
103+
return header;
104+
}
105+
renderMetadata() {
106+
const table = this.props.table;
107+
let cols;
108+
if (table.columns) {
109+
cols = table.columns.slice();
115110
if (this.state.sortColumns) {
116111
cols.sort((a, b) => a.name.toUpperCase() > b.name.toUpperCase());
117112
}
118-
metadata = (
113+
}
114+
const metadata = (
115+
<Collapse
116+
in={table.expanded}
117+
timeout={this.props.timeout}
118+
>
119119
<div>
120-
{header}
120+
{this.renderHeader()}
121121
<div className="table-columns">
122-
{cols.map((col) => {
122+
{cols && cols.map((col) => {
123123
let name = col.name;
124124
if (col.indexed) {
125125
name = <strong>{col.name}</strong>;
@@ -137,18 +137,17 @@ class TableElement extends React.PureComponent {
137137
<hr />
138138
</div>
139139
</div>
140-
);
141-
} else {
142-
buttonToggle = (
143-
<a
144-
href="#"
145-
onClick={(e) => { this.expandTable(e); }}
146-
>
147-
{table.name}
148-
<small className="m-l-5"><i className="fa fa-plus" /></small>
149-
</a>
150-
);
151-
}
140+
</Collapse>
141+
);
142+
return metadata;
143+
}
144+
removeFromStore() {
145+
this.props.actions.removeTable(this.props.table);
146+
}
147+
148+
render() {
149+
const table = this.props.table;
150+
152151
let keyLink;
153152
if (table.indexes && table.indexes.length > 0) {
154153
keyLink = (
@@ -171,52 +170,70 @@ class TableElement extends React.PureComponent {
171170
);
172171
}
173172
return (
174-
<div className="TableElement">
175-
<div className="clearfix">
176-
<div className="pull-left">
177-
{buttonToggle}
178-
</div>
179-
<div className="pull-right">
180-
<ButtonGroup className="ws-el-controls pull-right">
181-
{keyLink}
182-
<Link
183-
className={
184-
`fa fa-sort-${!this.state.sortColumns ? 'alpha' : 'numeric'}-asc ` +
185-
'pull-left sort-cols m-l-2'}
186-
onClick={this.toggleSortColumns.bind(this)}
187-
tooltip={
188-
!this.state.sortColumns ?
189-
'Sort columns alphabetically' :
190-
'Original table column order'}
173+
<Collapse
174+
in={this.state.expanded}
175+
timeout={this.props.timeout}
176+
transitionAppear
177+
onExited={this.removeFromStore.bind(this)}
178+
>
179+
<div className="TableElement">
180+
<div className="clearfix">
181+
<div className="pull-left">
182+
<a
191183
href="#"
192-
/>
193-
<Link
194-
className="fa fa-search-plus pull-left m-l-2"
195-
onClick={this.dataPreviewModal.bind(this)}
196-
tooltip="Data preview"
197-
href="#"
198-
/>
199-
<CopyToClipboard
200-
copyNode={
201-
<a className="fa fa-clipboard pull-left m-l-2" />
184+
className="table-name"
185+
onClick={(e) => { this.toggleTable(e); }}
186+
>
187+
<strong>{table.name}</strong>
188+
<small className="m-l-5">
189+
<i className={`fa fa-${table.expanded ? 'minus' : 'plus'}-square-o`} />
190+
</small>
191+
</a>
192+
</div>
193+
<div className="pull-right">
194+
<ButtonGroup className="ws-el-controls pull-right">
195+
{keyLink}
196+
<Link
197+
className={
198+
`fa fa-sort-${!this.state.sortColumns ? 'alpha' : 'numeric'}-asc ` +
199+
'pull-left sort-cols m-l-2'}
200+
onClick={this.toggleSortColumns.bind(this)}
201+
tooltip={
202+
!this.state.sortColumns ?
203+
'Sort columns alphabetically' :
204+
'Original table column order'}
205+
href="#"
206+
/>
207+
<Link
208+
className="fa fa-search-plus pull-left m-l-2"
209+
onClick={this.dataPreviewModal.bind(this)}
210+
tooltip="Data preview"
211+
href="#"
212+
/>
213+
{table.selectStar &&
214+
<CopyToClipboard
215+
copyNode={
216+
<a className="fa fa-clipboard pull-left m-l-2" />
217+
}
218+
text={table.selectStar}
219+
shouldShowText={false}
220+
tooltipText="Copy SELECT statement to clipboard"
221+
/>
202222
}
203-
text={table.selectStar}
204-
shouldShowText={false}
205-
tooltipText="Copy SELECT statement to clipboard"
206-
/>
207-
<Link
208-
className="fa fa-trash pull-left m-l-2"
209-
onClick={this.removeTable.bind(this)}
210-
tooltip="Remove from panel"
211-
href="#"
212-
/>
213-
</ButtonGroup>
223+
<Link
224+
className="fa fa-trash table-remove pull-left m-l-2"
225+
onClick={this.removeTable.bind(this)}
226+
tooltip="Remove from panel"
227+
href="#"
228+
/>
229+
</ButtonGroup>
230+
</div>
231+
</div>
232+
<div>
233+
{this.renderMetadata()}
214234
</div>
215235
</div>
216-
<div>
217-
{metadata}
218-
</div>
219-
</div>
236+
</Collapse>
220237
);
221238
}
222239
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import React from 'react';
22
import CopyQueryTabUrl from '../../../javascripts/SqlLab/components/CopyQueryTabUrl';
3-
import CopyToClipboard from '../../../javascripts/components/CopyToClipboard';
4-
import { shallow } from 'enzyme';
53
import { describe, it } from 'mocha';
64
import { expect } from 'chai';
75
import { initialState } from './fixtures';
@@ -10,16 +8,9 @@ describe('CopyQueryTabUrl', () => {
108
const mockedProps = {
119
queryEditor: initialState.queryEditors[0],
1210
};
13-
it('should be valid', () => {
14-
expect(React.isValidElement(<CopyQueryTabUrl />)).to.equal(true);
15-
});
16-
it('renders with props', () => {
11+
it('is valid with props', () => {
1712
expect(
1813
React.isValidElement(<CopyQueryTabUrl {...mockedProps} />)
1914
).to.equal(true);
2015
});
21-
it('renders a CopyToClipboard', () => {
22-
const wrapper = shallow(<CopyQueryTabUrl {...mockedProps} />);
23-
expect(wrapper.find(CopyToClipboard)).to.have.length(1);
24-
});
2516
});

‎caravel/assets/spec/javascripts/sqllab/TableElement_spec.jsx

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import React from 'react';
22
import Link from '../../../javascripts/SqlLab/components/Link';
33
import TableElement from '../../../javascripts/SqlLab/components/TableElement';
4-
import { table } from './fixtures';
4+
import { mockedActions, table } from './fixtures';
55
import { mount, shallow } from 'enzyme';
66
import { describe, it } from 'mocha';
77
import { expect } from 'chai';
88

99

1010
describe('TableElement', () => {
1111
const mockedProps = {
12+
actions: mockedActions,
1213
table,
14+
timeout: 0,
1315
};
1416
it('renders', () => {
1517
expect(
@@ -40,4 +42,19 @@ describe('TableElement', () => {
4042
expect(wrapper.state().sortColumns).to.equal(true);
4143
expect(wrapper.find('.col-name').first().text()).to.equal('last_login');
4244
});
45+
it('calls the collapseTable action', () => {
46+
const wrapper = mount(<TableElement {...mockedProps} />);
47+
expect(mockedActions.collapseTable.called).to.equal(false);
48+
wrapper.find('.table-name').simulate('click');
49+
expect(mockedActions.collapseTable.called).to.equal(true);
50+
});
51+
it('removes the table', () => {
52+
const wrapper = mount(<TableElement {...mockedProps} />);
53+
expect(wrapper.state().expanded).to.equal(true);
54+
wrapper.find('.table-remove').simulate('click');
55+
expect(wrapper.state().expanded).to.equal(false);
56+
setTimeout(() => {
57+
expect(mockedActions.removeTable.called).to.equal(true);
58+
}, 10);
59+
});
4360
});

‎caravel/assets/spec/javascripts/sqllab/fixtures.js

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import * as actions from '../../../javascripts/SqlLab/actions';
2+
import sinon from 'sinon';
3+
4+
export const mockedActions = sinon.stub(Object.assign({}, actions));
5+
16
export const alert = { bsStyle: 'danger', msg: 'Ooops', id: 'lksvmcx32' };
27
export const table = {
38
dbId: 1,
@@ -6,6 +11,11 @@ export const table = {
611
schema: 'caravel',
712
name: 'ab_user',
813
id: 'r11Vgt60',
14+
partitions: {
15+
cols: ['username'],
16+
latest: 'bob',
17+
partitionQuery: 'SHOW PARTITIONS FROM ab_user',
18+
},
919
indexes: [
1020
{
1121
unique: true,

0 commit comments

Comments
 (0)
Please sign in to comment.