Skip to content

Commit

Permalink
New "Time Series - Table" visualization (#3543)
Browse files Browse the repository at this point in the history
* [WiP] adding a new "Time Series - Table" viz

* Adding drag-n-drop to collection

* Using keys in arrays

* tests
  • Loading branch information
mistercrunch authored Oct 4, 2017
1 parent 645de38 commit bb0f69d
Show file tree
Hide file tree
Showing 20 changed files with 710 additions and 90 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 13 additions & 7 deletions superset/assets/javascripts/components/InfoTooltipWithTrigger.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { slugify } from '../modules/utils';

const propTypes = {
label: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired,
tooltip: PropTypes.string,
icon: PropTypes.string,
className: PropTypes.string,
onClick: PropTypes.func,
Expand All @@ -17,11 +17,21 @@ const defaultProps = {
className: 'text-muted',
placement: 'right',
};
const tooltipStyle = { wordWrap: 'break-word' };

export default function InfoTooltipWithTrigger({
label, tooltip, icon, className, onClick, placement, bsStyle }) {
const iconClass = `fa fa-${icon} ${className} ${bsStyle ? 'text-' + bsStyle : ''}`;
const tooltipStyle = { wordWrap: 'break-word' };
const iconEl = (
<i
className={iconClass}
onClick={onClick}
style={{ cursor: onClick ? 'pointer' : null }}
/>
);
if (!tooltip) {
return iconEl;
}
return (
<OverlayTrigger
placement={placement}
Expand All @@ -31,11 +41,7 @@ export default function InfoTooltipWithTrigger({
</Tooltip>
}
>
<i
className={iconClass}
onClick={onClick}
style={{ cursor: onClick ? 'pointer' : null }}
/>
{iconEl}
</OverlayTrigger>
);
}
Expand Down
21 changes: 14 additions & 7 deletions superset/assets/javascripts/components/MetricOption.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';

const propTypes = {
metric: PropTypes.object.isRequired,
showFormula: PropTypes.bool,
};
const defaultProps = {
showFormula: true,
};

export default function MetricOption({ metric }) {
export default function MetricOption({ metric, showFormula }) {
return (
<div>
<span className="m-r-5 option-label">
Expand All @@ -21,12 +25,14 @@ export default function MetricOption({ metric }) {
label={`descr-${metric.metric_name}`}
/>
}
<InfoTooltipWithTrigger
className="m-r-5 text-muted"
icon="question-circle-o"
tooltip={metric.expression}
label={`expr-${metric.metric_name}`}
/>
{showFormula &&
<InfoTooltipWithTrigger
className="m-r-5 text-muted"
icon="question-circle-o"
tooltip={metric.expression}
label={`expr-${metric.metric_name}`}
/>
}
{metric.warning_text &&
<InfoTooltipWithTrigger
className="m-r-5 text-danger"
Expand All @@ -38,3 +44,4 @@ export default function MetricOption({ metric }) {
</div>);
}
MetricOption.propTypes = propTypes;
MetricOption.defaultProps = defaultProps;
27 changes: 1 addition & 26 deletions superset/assets/javascripts/explore/components/Control.jsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';

import BoundsControl from './controls/BoundsControl';
import CheckboxControl from './controls/CheckboxControl';
import ColorSchemeControl from './controls/ColorSchemeControl';
import DatasourceControl from './controls/DatasourceControl';
import DateFilterControl from './controls/DateFilterControl';
import FilterControl from './controls/FilterControl';
import HiddenControl from './controls/HiddenControl';
import SelectAsyncControl from './controls/SelectAsyncControl';
import SelectControl from './controls/SelectControl';
import TextAreaControl from './controls/TextAreaControl';
import TextControl from './controls/TextControl';
import VizTypeControl from './controls/VizTypeControl';
import controlMap from './controls';

const controlMap = {
BoundsControl,
CheckboxControl,
DatasourceControl,
DateFilterControl,
FilterControl,
HiddenControl,
SelectControl,
TextAreaControl,
TextControl,
VizTypeControl,
ColorSchemeControl,
SelectAsyncControl,
};
const controlTypes = Object.keys(controlMap);

const propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,11 @@ import ControlHeader from '../ControlHeader';
import { t } from '../../../locales';

const propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string,
description: PropTypes.string,
onChange: PropTypes.func,
value: PropTypes.array,
};

const defaultProps = {
label: null,
description: null,
onChange: () => {},
value: [null, null],
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ListGroup, ListGroupItem } from 'react-bootstrap';
import shortid from 'shortid';
import {
SortableContainer, SortableHandle, SortableElement, arrayMove,
} from 'react-sortable-hoc';

import InfoTooltipWithTrigger from '../../../components/InfoTooltipWithTrigger';
import ControlHeader from '../ControlHeader';

const propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string,
description: PropTypes.string,
placeholder: PropTypes.string,
addTooltip: PropTypes.string,
itemGenerator: PropTypes.func,
keyAccessor: PropTypes.func,
onChange: PropTypes.func,
value: PropTypes.oneOfType([
PropTypes.array,
]),
isFloat: PropTypes.bool,
isInt: PropTypes.bool,
control: PropTypes.func,
};

const defaultProps = {
label: null,
description: null,
onChange: () => {},
placeholder: 'Empty collection',
itemGenerator: () => ({ key: shortid.generate() }),
keyAccessor: o => o.key,
value: [],
addTooltip: 'Add an item',
};
const SortableListGroupItem = SortableElement(ListGroupItem);
const SortableListGroup = SortableContainer(ListGroup);
const SortableDragger = SortableHandle(() => (
<i className="fa fa-bars text-primary" style={{ cursor: 'ns-resize' }} />));

export default class CollectionControl extends React.Component {
constructor(props) {
super(props);
this.onAdd = this.onAdd.bind(this);
}
onChange(i, value) {
Object.assign(this.props.value[i], value);
this.props.onChange(this.props.value);
}
onAdd() {
this.props.onChange(this.props.value.concat([this.props.itemGenerator()]));
}
onSortEnd({ oldIndex, newIndex }) {
this.props.onChange(arrayMove(this.props.value, oldIndex, newIndex));
}
removeItem(i) {
this.props.onChange(this.props.value.filter((o, ix) => i !== ix));
}
renderList() {
if (this.props.value.length === 0) {
return <div className="text-muted">{this.props.placeholder}</div>;
}
return (
<SortableListGroup
useDragHandle
lockAxis="y"
onSortEnd={this.onSortEnd.bind(this)}
>
{this.props.value.map((o, i) => (
<SortableListGroupItem
className="clearfix"
key={this.props.keyAccessor(o)}
index={i}
>
<div className="pull-left m-r-5">
<SortableDragger />
</div>
<div className="pull-left">
<this.props.control
{...o}
onChange={this.onChange.bind(this, i)}
/>
</div>
<div className="pull-right">
<InfoTooltipWithTrigger
icon="times"
label="remove-item"
tooltip="remove item"
bsStyle="primary"
onClick={this.removeItem.bind(this, i)}
/>
</div>
</SortableListGroupItem>))}
</SortableListGroup>
);
}
render() {
return (
<div>
<ControlHeader {...this.props} />
{this.renderList()}
<InfoTooltipWithTrigger
icon="plus-circle"
label="add-item"
tooltip={this.props.addTooltip}
bsStyle="primary"
className="fa-lg"
onClick={this.onAdd}
/>
</div>
);
}
}

CollectionControl.propTypes = propTypes;
CollectionControl.defaultProps = defaultProps;
Loading

0 comments on commit bb0f69d

Please sign in to comment.