Skip to content

Commit

Permalink
adjusted transitions and made the box expandable
Browse files Browse the repository at this point in the history
  • Loading branch information
GabeLoins committed Apr 13, 2018
1 parent 1ac2d39 commit 6a8dde6
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 41 deletions.
7 changes: 5 additions & 2 deletions superset/assets/javascripts/explore/AdhocMetric.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { sqlaAutoGeneratedMetricRegex } from './constants';

export const EXPRESSION_TYPES = {
SIMPLE: 'SIMPLE',
SQL: 'SQL',
};

function inferSqlExpressionColumn(adhocMetric) {
if (adhocMetric.sqlExpression) {
if (adhocMetric.sqlExpression && sqlaAutoGeneratedMetricRegex.test(adhocMetric.sqlExpression)) {
const indexFirstCloseParen = adhocMetric.sqlExpression.indexOf(')');
const indexPairedOpenParen =
adhocMetric.sqlExpression.substring(0, indexFirstCloseParen).lastIndexOf('(');
Expand All @@ -16,7 +18,7 @@ function inferSqlExpressionColumn(adhocMetric) {
}

function inferSqlExpressionAggregate(adhocMetric) {
if (adhocMetric.sqlExpression) {
if (adhocMetric.sqlExpression && sqlaAutoGeneratedMetricRegex.test(adhocMetric.sqlExpression)) {
const indexFirstOpenParen = adhocMetric.sqlExpression.indexOf('(');
if (indexFirstOpenParen > 0) {
return adhocMetric.sqlExpression.substring(0, indexFirstOpenParen);
Expand All @@ -29,6 +31,7 @@ export default class AdhocMetric {
constructor(adhocMetric) {
this.expressionType = adhocMetric.expressionType || EXPRESSION_TYPES.SIMPLE;
if (this.expressionType === EXPRESSION_TYPES.SIMPLE) {
// try to be clever in the case of transitioning from Sql expression back to simple expression
const inferredColumn = inferSqlExpressionColumn(adhocMetric);
this.column = adhocMetric.column || (inferredColumn && { column_name: inferredColumn });
this.aggregate = adhocMetric.aggregate || inferSqlExpressionAggregate(adhocMetric);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const propTypes = {
adhocMetric: PropTypes.instanceOf(AdhocMetric).isRequired,
onChange: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onResize: PropTypes.func.isRequired,
columns: PropTypes.arrayOf(columnType),
datasourceType: PropTypes.string,
};
Expand All @@ -31,6 +32,9 @@ const defaultProps = {
columns: [],
};

const startingWidth = 300;
const startingHeight = 180;

export default class AdhocMetricEditPopover extends React.Component {
constructor(props) {
super(props);
Expand All @@ -39,7 +43,16 @@ export default class AdhocMetricEditPopover extends React.Component {
this.onAggregateChange = this.onAggregateChange.bind(this);
this.onSqlExpressionChange = this.onSqlExpressionChange.bind(this);
this.onLabelChange = this.onLabelChange.bind(this);
this.state = { adhocMetric: this.props.adhocMetric }; this.selectProps = { multi: false,
this.onDragDown = this.onDragDown.bind(this);
this.onMouseMove = this.onMouseMove.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.state = {
adhocMetric: this.props.adhocMetric,
width: startingWidth,
height: startingHeight,
};
this.selectProps = {
multi: false,
name: 'select-column',
labelKey: 'label',
autosize: false,
Expand All @@ -57,6 +70,12 @@ export default class AdhocMetricEditPopover extends React.Component {
};
langTools.setCompleters([completer]);
}
document.addEventListener('mouseup', this.onMouseUp);
}

componentWillUnmount() {
document.removeEventListener('mouseup', this.onMouseUp);
document.removeEventListener('mousemove', this.onMouseMove);
}

onSave() {
Expand All @@ -73,17 +92,21 @@ export default class AdhocMetricEditPopover extends React.Component {

onAggregateChange(aggregate) {
// we construct this object explicitly to overwrite the value in the case aggregate is null
this.setState({ adhocMetric: this.state.adhocMetric.duplicateWith({
aggregate: aggregate && aggregate.aggregate,
expressionType: EXPRESSION_TYPES.SIMPLE,
}) });
this.setState({
adhocMetric: this.state.adhocMetric.duplicateWith({
aggregate: aggregate && aggregate.aggregate,
expressionType: EXPRESSION_TYPES.SIMPLE,
}),
});
}

onSqlExpressionChange(sqlExpression) {
this.setState({ adhocMetric: this.state.adhocMetric.duplicateWith({
sqlExpression,
expressionType: EXPRESSION_TYPES.SQL,
}) });
this.setState({
adhocMetric: this.state.adhocMetric.duplicateWith({
sqlExpression,
expressionType: EXPRESSION_TYPES.SQL,
}),
});
}

onLabelChange(e) {
Expand All @@ -94,12 +117,33 @@ export default class AdhocMetricEditPopover extends React.Component {
});
}

onDragDown(e) {
this.dragStartX = e.clientX;
this.dragStartY = e.clientY;
this.dragStartWidth = this.state.width;
this.dragStartHeight = this.state.height;
document.addEventListener('mousemove', this.onMouseMove);
}

onMouseMove(e) {
this.props.onResize();
this.setState({
width: Math.max(this.dragStartWidth + (e.clientX - this.dragStartX), startingWidth),
height: Math.max(this.dragStartHeight + (e.clientY - this.dragStartY) * 2, startingHeight),
});
}

onMouseUp() {
document.removeEventListener('mousemove', this.onMouseMove);
}

render() {
const {
adhocMetric: propsAdhocMetric,
columns,
onChange,
onClose,
onResize,
datasourceType,
...popoverProps
} = this.props;
Expand Down Expand Up @@ -143,7 +187,7 @@ export default class AdhocMetricEditPopover extends React.Component {
);

const stateIsValid = adhocMetric.isValid();
const hasUnsavedChanges = adhocMetric.equals(propsAdhocMetric);
const hasUnsavedChanges = !adhocMetric.equals(propsAdhocMetric);

return (
<Popover
Expand All @@ -155,6 +199,7 @@ export default class AdhocMetricEditPopover extends React.Component {
id="adhoc-metric-edit-tabs"
defaultActiveKey={adhocMetric.expressionType}
className="adhoc-metric-edit-tabs"
style={{ height: this.state.height, width: this.state.width }}
>
<Tab className="adhoc-metric-edit-tab" eventKey={EXPRESSION_TYPES.SIMPLE} title="Simple">
<FormGroup>
Expand All @@ -173,7 +218,7 @@ export default class AdhocMetricEditPopover extends React.Component {
<AceEditor
mode="sql"
theme="github"
height="120px"
height={(this.state.height - 40) + 'px'}
onChange={this.onSqlExpressionChange}
width="100%"
showGutter={false}
Expand All @@ -185,18 +230,22 @@ export default class AdhocMetricEditPopover extends React.Component {
</Tab>
}
</Tabs>
<Button
disabled={!stateIsValid}
bsStyle={(hasUnsavedChanges || !stateIsValid) ? 'default' : 'primary'}
bsSize="small"
className="m-r-5"
onClick={this.onSave}
>
Save
</Button>
<Button bsSize="small" onClick={this.props.onClose}>Close</Button>
<div>
<Button
disabled={!stateIsValid}
bsStyle={(hasUnsavedChanges && stateIsValid) ? 'primary' : 'default'}
bsSize="small"
className="m-r-5"
onClick={this.onSave}
>
Save
</Button>
<Button bsSize="small" onClick={this.props.onClose}>Close</Button>
<i onMouseDown={this.onDragDown} className="glyphicon glyphicon-resize-full edit-popover-resize" />
</div>
</Popover>
);
}
} AdhocMetricEditPopover.propTypes = propTypes;
}
AdhocMetricEditPopover.propTypes = propTypes;
AdhocMetricEditPopover.defaultProps = defaultProps;
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ export default class AdhocMetricOption extends React.PureComponent {
constructor(props) {
super(props);
this.closeMetricEditOverlay = this.closeMetricEditOverlay.bind(this);
this.onPopoverResize = this.onPopoverResize.bind(this);
}

onPopoverResize() {
this.forceUpdate();
}

closeMetricEditOverlay() {
Expand All @@ -28,6 +33,7 @@ export default class AdhocMetricOption extends React.PureComponent {
const { adhocMetric } = this.props;
const overlay = (
<AdhocMetricEditPopover
onResize={this.onPopoverResize}
adhocMetric={adhocMetric}
onChange={this.props.onMetricEdit}
onClose={this.closeMetricEditOverlay}
Expand All @@ -44,6 +50,7 @@ export default class AdhocMetricOption extends React.PureComponent {
disabled
overlay={overlay}
rootClose
shouldUpdatePosition
defaultOverlayShown={!adhocMetric.fromFormData}
>
<Label style={{ margin: this.props.multi ? 0 : 3, cursor: 'pointer' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import AdhocMetric from '../../AdhocMetric';
import columnType from '../../propTypes/columnType';
import savedMetricType from '../../propTypes/savedMetricType';
import adhocMetricType from '../../propTypes/adhocMetricType';
import { AGGREGATES } from '../../constants';
import {
AGGREGATES,
sqlaAutoGeneratedMetricRegex,
druidAutoGeneratedMetricRegex,
} from '../../constants';

const propTypes = {
name: PropTypes.string.isRequired,
Expand Down Expand Up @@ -74,9 +78,6 @@ function getDefaultAggregateForColumn(column) {
return null;
}

const sqlaAutoGeneratedMetricRegex = /^(LONG|DOUBLE|FLOAT)?(SUM|AVG|MAX|MIN|COUNT)\([A-Z_][A-Z0-9_]*\)$/i;
const druidAutoGeneratedMetricRegex = /^(LONG|DOUBLE|FLOAT)?(SUM|MAX|MIN|COUNT)\([A-Z_][A-Z0-9_]*\)$/i;

export default class MetricsControl extends React.PureComponent {
constructor(props) {
super(props);
Expand Down
3 changes: 3 additions & 0 deletions superset/assets/javascripts/explore/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ export const AGGREGATES = {
SUM: 'SUM',
};

export const sqlaAutoGeneratedMetricRegex = /^(LONG|DOUBLE|FLOAT)?(SUM|AVG|MAX|MIN|COUNT)\([A-Z_][A-Z0-9_]*\)$/i;
export const druidAutoGeneratedMetricRegex = /^(LONG|DOUBLE|FLOAT)?(SUM|MAX|MIN|COUNT)\([A-Z_][A-Z0-9_]*\)$/i;

25 changes: 16 additions & 9 deletions superset/assets/javascripts/explore/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -139,18 +139,25 @@
overflow: auto;
}

.adhoc-sql-expression-input {
width: 217px;
}

.adhoc-metric-edit-tabs > .nav-tabs {
margin-bottom: 6px;
}

.adhoc-metric-edit-tabs > .tab-content {
height: 140px;
width: 260px;
}
.adhoc-metric-edit-tabs > .nav-tabs > li > a {
padding: 0 3px 0 3px;
padding: 4px 4px 4px 4px;
}

.edit-popover-resize {
transform: scaleX(-1);
-moz-transform: scaleX(-1);
-webkit-transform: scaleX(-1);
-ms-transform: scaleX(-1);
float: right;
margin-top: 18px;
margin-right: -10px;
cursor: nwse-resize;
}

#metrics-edit-popover {
max-width: none;
}
8 changes: 4 additions & 4 deletions superset/assets/spec/javascripts/explore/AdhocMetric_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ describe('AdhocMetric', () => {
expect(adhocMetric2.label).to.equal('label1');
});

it('can determines if it is valid', () => {
it('can determine if it is valid', () => {
const adhocMetric1 = new AdhocMetric({
expressionType: EXPRESSION_TYPES.SIMPLE,
column: valueColumn,
Expand Down Expand Up @@ -145,7 +145,7 @@ describe('AdhocMetric', () => {
expect(adhocMetric5.isValid()).to.be.false;
});

it('can translate back from sql expressions to simple expressions', () => {
it('can translate back from sql expressions to simple expressions when possible', () => {
const adhocMetric = new AdhocMetric({
expressionType: EXPRESSION_TYPES.SQL,
sqlExpression: 'AVG(my_column)',
Expand All @@ -161,8 +161,8 @@ describe('AdhocMetric', () => {
hasCustomLabel: true,
label: 'label1',
});
expect(adhocMetric2.inferSqlExpressionColumn()).to.equal('my_column');
expect(adhocMetric2.inferSqlExpressionAggregate()).to.equal('AVG');
expect(adhocMetric2.inferSqlExpressionColumn()).to.equal(null);
expect(adhocMetric2.inferSqlExpressionAggregate()).to.equal(null);
});

it('will infer columns and aggregates when converting to a simple expression', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,15 @@ describe('AdhocMetricEditPopover', () => {
wrapper.instance().onColumnChange({ column: columns[1] });
expect(wrapper.find(Button).find({ bsStyle: 'primary' })).to.have.lengthOf(1);
});

it('will initiate a drag when clicked', () => {
const { wrapper } = setup();
wrapper.instance().onDragDown = sinon.spy();
wrapper.instance().forceUpdate();

expect(wrapper.find('i.glyphicon-resize-full')).to.have.lengthOf(1);
expect(wrapper.instance().onDragDown.calledOnce).to.be.false;
wrapper.find('i.glyphicon-resize-full').simulate('mouseDown');
expect(wrapper.instance().onDragDown.calledOnce).to.be.true;
});
});

0 comments on commit 6a8dde6

Please sign in to comment.