Skip to content

Commit

Permalink
[explore V2] render all control panels and fields dynamically for eac…
Browse files Browse the repository at this point in the history
…h vis type (#1493)

* export functions directly rather than object at the bottom

* move viztypes to controlPanelMappings, add fieldset rows and section data

* for each viz type, render a controlPanelsContainer, controlPanelSections, FieldSetRows, and FieldsSets

* add comments, move mappings to store

* organize store and add default sections

* render all the needed sections

* add tooltip to sections

* remove console log

* use only panel panel-default, not panel-body, no need the padding

* render fields for all fields in field set

* add the rest of the control panel sections and field overrides

* fix naming

* add fieldTypes array

* don't use default section

* pass only needed state via mapStateToProps

* fix code climate errors

* linting

* move field components to their own files

* render field sets as lists

* fix field components

* use SFC

* update modal trigger test to be more accurate

* add FieldSetRow test

* add test for controlpanelsContainer

* fix test

* make code climate happy

* add freeform select field
  • Loading branch information
Alanna Scott authored Nov 2, 2016
1 parent 1b124bf commit 38d3075
Show file tree
Hide file tree
Showing 17 changed files with 2,069 additions and 81 deletions.
20 changes: 20 additions & 0 deletions caravel/assets/javascripts/components/InfoTooltipWithTrigger.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { PropTypes } from 'react';
import { Tooltip, OverlayTrigger } from 'react-bootstrap';

const propTypes = {
label: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired,
};

export default function InfoTooltipWithTrigger({ label, tooltip }) {
return (
<OverlayTrigger
placement="right"
overlay={<Tooltip id={`${label}-tooltip`}>{tooltip}</Tooltip>}
>
<i className="fa fa-question-circle-o" />
</OverlayTrigger>
);
}

InfoTooltipWithTrigger.propTypes = propTypes;
24 changes: 24 additions & 0 deletions caravel/assets/javascripts/explorev2/components/CheckboxField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { PropTypes } from 'react';
import { Checkbox } from 'react-bootstrap';
import ControlLabelWithTooltip from './ControlLabelWithTooltip';

const propTypes = {
label: PropTypes.string,
description: PropTypes.string,
};

const defaultProps = {
label: null,
description: null,
};

export default function CheckboxField({ label, description }) {
return (
<Checkbox>
<ControlLabelWithTooltip label={label} description={description} />
</Checkbox>
);
}

CheckboxField.propTypes = propTypes;
CheckboxField.defaultProps = defaultProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { PropTypes } from 'react';
import { ControlLabel } from 'react-bootstrap';
import InfoTooltipWithTrigger from '../../components/InfoTooltipWithTrigger';

const propTypes = {
label: PropTypes.string,
description: PropTypes.string,
};

const defaultProps = {
label: null,
description: null,
};

export default function ControlLabelWithTooltip({ label, description }) {
return (
<ControlLabel>
{label} &nbsp;
{description &&
<InfoTooltipWithTrigger label={label} tooltip={description} />
}
</ControlLabel>
);
}

ControlLabelWithTooltip.propTypes = propTypes;
ControlLabelWithTooltip.defaultProps = defaultProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { PropTypes } from 'react';
import { Panel } from 'react-bootstrap';
import InfoTooltipWithTrigger from '../../components/InfoTooltipWithTrigger';

const propTypes = {
label: PropTypes.string,
description: PropTypes.string,
tooltip: PropTypes.string,
children: PropTypes.node.isRequired,
};

const defaultProps = {
label: null,
description: null,
tooltip: null,
};

export default class ControlPanelSection extends React.Component {
header() {
const { label, tooltip } = this.props;
let header;
if (label) {
header = (
<div className="panel-title">
{label} &nbsp;
{tooltip && <InfoTooltipWithTrigger label={label} tooltip={tooltip} />}
</div>
);
}
return header;
}

render() {
return (
<Panel header={this.header()}>
{this.props.children}
</Panel>
);
}
}

ControlPanelSection.propTypes = propTypes;
ControlPanelSection.defaultProps = defaultProps;
Original file line number Diff line number Diff line change
@@ -1,40 +1,81 @@
import React from 'react';
import React, { PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import * as actions from '../actions/exploreActions';
import { connect } from 'react-redux';
import { Panel } from 'react-bootstrap';
import { DefaultControls, VIZ_CONTROL_MAPPING } from '../constants';
import { visTypes, commonControlPanelSections } from '../stores/store';
import ControlPanelSection from './ControlPanelSection';
import FieldSetRow from './FieldSetRow';

const propTypes = {
vizType: React.PropTypes.string,
vizType: PropTypes.string,
datasourceId: PropTypes.number.isRequired,
datasourceType: PropTypes.string.isRequired,
actions: PropTypes.object.isRequired,
};

const defaultProps = {
vizType: null,
};

function ControlPanelsContainer(props) {
return (
<Panel>
<div className="scrollbar-container">
<div className="scrollbar-content">
{DefaultControls}
{VIZ_CONTROL_MAPPING[props.vizType]}
function getSectionsToRender(vizSections) {
const { datasourceAndVizType, sqlClause } = commonControlPanelSections;
const sectionsToRender = [datasourceAndVizType].concat(vizSections, sqlClause);
return sectionsToRender;
}

class ControlPanelsContainer extends React.Component {
componentWillMount() {
const { datasourceId, datasourceType } = this.props;
if (datasourceId) {
this.props.actions.setFormOpts(datasourceId, datasourceType);
}
}

render() {
const viz = visTypes[this.props.vizType];
const sectionsToRender = getSectionsToRender(viz.controlPanelSections);

return (
<Panel>
<div className="scrollbar-container">
<div className="scrollbar-content">
{sectionsToRender.map((section) => (
<ControlPanelSection
key={section.label}
label={section.label}
tooltip={section.description}
>
{section.fieldSetRows.map((fieldSets, i) => (
<FieldSetRow key={`${section.label}-fieldSetRow-${i}`} fieldSets={fieldSets} />
))}
</ControlPanelSection>
))}
{/* TODO: add filters section */}
</div>
</div>
</div>
</Panel>
);
</Panel>
);
}
}

ControlPanelsContainer.propTypes = propTypes;
ControlPanelsContainer.defaultProps = defaultProps;

function mapStateToProps(state) {
return {
datasourceId: state.datasourceId,
datasourceType: state.datasourceType,
vizType: state.viz.formData.vizType,
};
}

function mapDispatchToProps() {
return {};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}

export { ControlPanelsContainer };

export default connect(mapStateToProps, mapDispatchToProps)(ControlPanelsContainer);
63 changes: 63 additions & 0 deletions caravel/assets/javascripts/explorev2/components/FieldSet.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { PropTypes } from 'react';
import TextField from './TextField';
import CheckboxField from './CheckboxField';
import TextAreaField from './TextAreaField';
import SelectField from './SelectField';
import { fieldTypes } from '../stores/store';

const propTypes = {
type: PropTypes.oneOf(fieldTypes).isRequired,
label: PropTypes.string.isRequired,
choices: PropTypes.arrayOf(PropTypes.array),
description: PropTypes.string,
places: PropTypes.number,
validators: PropTypes.any,
};

const defaultProps = {
choices: null,
description: null,
places: null,
validators: null,
};

export default class FieldSet extends React.Component {
renderCheckBoxField() {
return <CheckboxField label={this.props.label} description={this.props.description} />;
}

renderTextAreaField() {
return <TextAreaField label={this.props.label} description={this.props.description} />;
}

renderSelectField() {
return <SelectField label={this.props.label} description={this.props.description} />;
}

renderTextField() {
return <TextField label={this.props.label} description={this.props.description} />;
}

render() {
const type = this.props.type;
let html;

if (type === 'CheckboxField') {
html = this.renderCheckBoxField();
} else if (type === 'SelectField' ||
type === 'SelectCustomMultiField' ||
type === 'SelectMultipleSortableField' ||
type === 'FreeFormSelectField') {
html = this.renderSelectField();
} else if (type === 'TextField' || type === 'IntegerField') {
html = this.renderTextField();
} else if (type === 'TextAreaField') {
this.renderTextAreaField();
}

return html;
}
}

FieldSet.propTypes = propTypes;
FieldSet.defaultProps = defaultProps;
17 changes: 17 additions & 0 deletions caravel/assets/javascripts/explorev2/components/FieldSetRow.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { PropTypes } from 'react';
import FieldSet from './FieldSet';
import { fields } from '../stores/store';

const propTypes = {
fieldSets: PropTypes.array.isRequired,
};

export default function FieldSetRow({ fieldSets }) {
return (
<ul className="list-unstyled">
{fieldSets.map((fs) => <li key={fs}><FieldSet {...fields[fs]} /></li>)}
</ul>
);
}

FieldSetRow.propTypes = propTypes;
28 changes: 28 additions & 0 deletions caravel/assets/javascripts/explorev2/components/SelectField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { PropTypes } from 'react';
import { FormGroup, FormControl } from 'react-bootstrap';
import ControlLabelWithTooltip from './ControlLabelWithTooltip';

const propTypes = {
label: PropTypes.string,
description: PropTypes.string,
};

const defaultProps = {
label: null,
description: null,
};

export default function SelectField({ label, description }) {
return (
<FormGroup controlId={`formControlsSelect-${label}`}>
<ControlLabelWithTooltip label={label} description={description} />
<FormControl componentClass="select" placeholder="select">
<option value="select">select</option>
<option value="other">...</option>
</FormControl>
</FormGroup>
);
}

SelectField.propTypes = propTypes;
SelectField.defaultProps = defaultProps;
25 changes: 25 additions & 0 deletions caravel/assets/javascripts/explorev2/components/TextAreaField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { PropTypes } from 'react';
import { FormGroup, FormControl } from 'react-bootstrap';
import ControlLabelWithTooltip from './ControlLabelWithTooltip';

const propTypes = {
label: PropTypes.string,
description: PropTypes.string,
};

const defaultProps = {
label: null,
description: null,
};

export default function TextAreaField({ label, description }) {
return (
<FormGroup controlId="formControlsTextarea">
<ControlLabelWithTooltip label={label} description={description} />
<FormControl componentClass="textarea" placeholder="textarea" />
</FormGroup>
);
}

TextAreaField.propTypes = propTypes;
TextAreaField.defaultProps = defaultProps;
25 changes: 25 additions & 0 deletions caravel/assets/javascripts/explorev2/components/TextField.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { PropTypes } from 'react';
import { FormGroup, FormControl } from 'react-bootstrap';
import ControlLabelWithTooltip from './ControlLabelWithTooltip';

const propTypes = {
label: PropTypes.string,
description: PropTypes.string,
};

const defaultProps = {
label: null,
description: null,
};

export default function TextField({ label, description }) {
return (
<FormGroup controlId="formInlineName">
<ControlLabelWithTooltip label={label} description={description} />
<FormControl type="text" placeholder="" />
</FormGroup>
);
}

TextField.propTypes = propTypes;
TextField.defaultProps = defaultProps;
Loading

0 comments on commit 38d3075

Please sign in to comment.