refactor: Bootstrap to AntD - Form - iteration 3 (#14502)

This commit is contained in:
Michael S. Molina 2021-05-08 16:34:52 -03:00 committed by GitHub
parent 4f000cc8d1
commit 79ff96269b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 176 additions and 179 deletions

View File

@ -20,7 +20,7 @@
import React from 'react'; import React from 'react';
import sinon from 'sinon'; import sinon from 'sinon';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { FormGroup } from 'react-bootstrap'; import { FormItem } from 'src/components/Form';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { AGGREGATES } from 'src/explore/constants'; import { AGGREGATES } from 'src/explore/constants';
@ -67,7 +67,7 @@ function setup(overrides) {
describe('AdhocMetricEditPopover', () => { describe('AdhocMetricEditPopover', () => {
it('renders a popover with edit metric form contents', () => { it('renders a popover with edit metric form contents', () => {
const { wrapper } = setup(); const { wrapper } = setup();
expect(wrapper.find(FormGroup)).toHaveLength(4); expect(wrapper.find(FormItem)).toHaveLength(3);
expect(wrapper.find(Button)).toHaveLength(2); expect(wrapper.find(Button)).toHaveLength(2);
}); });

View File

@ -17,10 +17,10 @@
* under the License. * under the License.
*/ */
import React from 'react'; import React from 'react';
import { FormControl } from 'react-bootstrap';
import sinon from 'sinon'; import sinon from 'sinon';
import { styledMount as mount } from 'spec/helpers/theming'; import { styledMount as mount } from 'spec/helpers/theming';
import BoundsControl from 'src/explore/components/controls/BoundsControl'; import BoundsControl from 'src/explore/components/controls/BoundsControl';
import { Input } from 'src/common/components';
const defaultProps = { const defaultProps = {
name: 'y_axis_bounds', name: 'y_axis_bounds',
@ -35,13 +35,13 @@ describe('BoundsControl', () => {
wrapper = mount(<BoundsControl {...defaultProps} />); wrapper = mount(<BoundsControl {...defaultProps} />);
}); });
it('renders two FormControls', () => { it('renders two Input', () => {
expect(wrapper.find(FormControl)).toHaveLength(2); expect(wrapper.find(Input)).toHaveLength(2);
}); });
it('errors on non-numeric', () => { it('errors on non-numeric', () => {
wrapper wrapper
.find(FormControl) .find(Input)
.first() .first()
.simulate('change', { target: { value: 's' } }); .simulate('change', { target: { value: 's' } });
expect(defaultProps.onChange.calledWith([null, null])).toBe(true); expect(defaultProps.onChange.calledWith([null, null])).toBe(true);
@ -51,11 +51,11 @@ describe('BoundsControl', () => {
}); });
it('casts to numeric', () => { it('casts to numeric', () => {
wrapper wrapper
.find(FormControl) .find(Input)
.first() .first()
.simulate('change', { target: { value: '1' } }); .simulate('change', { target: { value: '1' } });
wrapper wrapper
.find(FormControl) .find(Input)
.last() .last()
.simulate('change', { target: { value: '5' } }); .simulate('change', { target: { value: '5' } });
expect(defaultProps.onChange.calledWith([1, 5])).toBe(true); expect(defaultProps.onChange.calledWith([1, 5])).toBe(true);

View File

@ -17,12 +17,12 @@
* under the License. * under the License.
*/ */
import React from 'react'; import React from 'react';
import { FormControl } from 'react-bootstrap';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import * as sinon from 'sinon'; import * as sinon from 'sinon';
import SaveQuery from 'src/SqlLab/components/SaveQuery'; import SaveQuery from 'src/SqlLab/components/SaveQuery';
import Modal from 'src/components/Modal'; import Modal from 'src/components/Modal';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { FormItem } from 'src/components/Form';
describe('SavedQuery', () => { describe('SavedQuery', () => {
const mockedProps = { const mockedProps = {
@ -52,11 +52,11 @@ describe('SavedQuery', () => {
expect(modal.find('[data-test="cancel-query"]')).toHaveLength(1); expect(modal.find('[data-test="cancel-query"]')).toHaveLength(1);
}); });
it('has 2 FormControls', () => { it('has 2 FormItem', () => {
const wrapper = shallow(<SaveQuery {...mockedProps} />); const wrapper = shallow(<SaveQuery {...mockedProps} />);
const modal = wrapper.find(Modal); const modal = wrapper.find(Modal);
expect(modal.find(FormControl)).toHaveLength(2); expect(modal.find(FormItem)).toHaveLength(2);
}); });
// eslint-disable-next-line jest/no-disabled-tests // eslint-disable-next-line jest/no-disabled-tests
it.skip('has a save button if this is a new query', () => { it.skip('has a save button if this is a new query', () => {

View File

@ -17,12 +17,10 @@
* under the License. * under the License.
*/ */
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Row, Col } from 'src/common/components'; import { Row, Col, Input, TextArea } from 'src/common/components';
import { FormControl, FormGroup } from 'react-bootstrap';
import { t, supersetTheme, styled } from '@superset-ui/core'; import { t, supersetTheme, styled } from '@superset-ui/core';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { FormLabel } from 'src/components/Form'; import { Form, FormItem } from 'src/components/Form';
import Modal from 'src/components/Modal'; import Modal from 'src/components/Modal';
import Icon from 'src/components/Icon'; import Icon from 'src/components/Icon';
@ -100,12 +98,12 @@ export default function SaveQuery({
close(); close();
}; };
const onLabelChange = (e: React.FormEvent<FormControl>) => { const onLabelChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setLabel((e.target as HTMLInputElement).value); setLabel(e.target.value);
}; };
const onDescriptionChange = (e: React.FormEvent<FormControl>) => { const onDescriptionChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
setDescription((e.target as HTMLInputElement).value); setDescription(e.target.value);
}; };
const toggleSave = () => { const toggleSave = () => {
@ -113,27 +111,24 @@ export default function SaveQuery({
}; };
const renderModalBody = () => ( const renderModalBody = () => (
<FormGroup bsSize="small"> <Form layout="vertical">
<Row> <Row>
<Col xs={24}> <Col xs={24}>
<small> <FormItem label={t('Name')}>
<FormLabel htmlFor="embed-height">{t('Name')}</FormLabel> <Input type="text" value={label} onChange={onLabelChange} />
</small> </FormItem>
<FormControl type="text" value={label} onChange={onLabelChange} />
</Col> </Col>
</Row> </Row>
<br /> <br />
<Row> <Row>
<Col xs={24}> <Col xs={24}>
<small> <FormItem label={t('Description')}>
<FormLabel htmlFor="embed-height">{t('Description')}</FormLabel> <TextArea
</small> rows={4}
<FormControl value={description}
rows={5} onChange={onDescriptionChange}
componentClass="textarea" />
value={description} </FormItem>
onChange={onDescriptionChange}
/>
</Col> </Col>
</Row> </Row>
{saveQueryWarning && ( {saveQueryWarning && (
@ -149,7 +144,7 @@ export default function SaveQuery({
</div> </div>
</> </>
)} )}
</FormGroup> </Form>
); );
return ( return (

View File

@ -17,13 +17,12 @@
* under the License. * under the License.
*/ */
import React, { FunctionComponent, useState } from 'react'; import React, { FunctionComponent, useState } from 'react';
import Form, { FormProps, FormValidation } from 'react-jsonschema-form'; import SchemaForm, { FormProps, FormValidation } from 'react-jsonschema-form';
import { Row, Col } from 'src/common/components'; import { Row, Col, Input, TextArea } from 'src/common/components';
import { FormControl, FormGroup } from 'react-bootstrap';
import { t, styled } from '@superset-ui/core'; import { t, styled } from '@superset-ui/core';
import * as chrono from 'chrono-node'; import * as chrono from 'chrono-node';
import ModalTrigger from 'src/components/ModalTrigger'; import ModalTrigger from 'src/components/ModalTrigger';
import { FormLabel } from 'src/components/Form'; import { Form, FormItem } from 'src/components/Form';
import './ScheduleQueryButton.less'; import './ScheduleQueryButton.less';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
@ -139,42 +138,52 @@ const ScheduleQueryButton: FunctionComponent<ScheduleQueryButtonProps> = ({
}; };
const renderModalBody = () => ( const renderModalBody = () => (
<FormGroup> <Form layout="vertical">
<StyledRow> <StyledRow>
<Col xs={24}> <Col xs={24}>
<FormLabel className="control-label" htmlFor="embed-height"> <FormItem label={t('Label')}>
{t('Label')} <Input
</FormLabel> type="text"
<FormControl placeholder={t('Label for your query')}
type="text" value={label}
placeholder={t('Label for your query')} onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
value={label} setLabel(event.target.value)
onChange={(event: any) => setLabel(event.target?.value)} }
/> />
</FormItem>
</Col> </Col>
</StyledRow> </StyledRow>
<StyledRow> <StyledRow>
<Col xs={24}> <Col xs={24}>
<FormLabel className="control-label" htmlFor="embed-height"> <FormItem label={t('Description')}>
{t('Description')} <TextArea
</FormLabel> rows={4}
<FormControl placeholder={t('Write a description for your query')}
componentClass="textarea" value={description}
placeholder={t('Write a description for your query')} onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) =>
value={description} setDescription(event.target.value)
onChange={(event: any) => setDescription(event.target?.value)} }
/> />
</FormItem>
</Col> </Col>
</StyledRow> </StyledRow>
<Row> <Row>
<Col xs={24}> <Col xs={24}>
<div className="json-schema"> <div className="json-schema">
<Form <SchemaForm
schema={getJSONSchema()} schema={getJSONSchema()}
uiSchema={getUISchema} uiSchema={getUISchema}
onSubmit={onScheduleSubmit} onSubmit={onScheduleSubmit}
validate={getValidator()} validate={getValidator()}
/> >
<Button
buttonStyle="primary"
htmlType="submit"
css={{ float: 'right' }}
>
Submit
</Button>
</SchemaForm>
</div> </div>
</Col> </Col>
</Row> </Row>
@ -185,7 +194,7 @@ const ScheduleQueryButton: FunctionComponent<ScheduleQueryButtonProps> = ({
</Col> </Col>
</Row> </Row>
)} )}
</FormGroup> </Form>
); );
return ( return (

View File

@ -89,11 +89,13 @@ export const Menu = Object.assign(AntdMenu, {
}); });
export const Input = styled(AntdInput)` export const Input = styled(AntdInput)`
&[type='text'], border: 1px solid ${({ theme }) => theme.colors.secondary.light3};
&[type='textarea'] { border-radius: ${({ theme }) => theme.borderRadius}px;
border: 1px solid ${({ theme }) => theme.colors.secondary.light3}; `;
border-radius: ${({ theme }) => theme.borderRadius}px;
} export const TextArea = styled(AntdInput.TextArea)`
border: 1px solid ${({ theme }) => theme.colors.secondary.light3};
border-radius: ${({ theme }) => theme.borderRadius}px;
`; `;
export const NoAnimationDropdown = (props: DropDownProps) => ( export const NoAnimationDropdown = (props: DropDownProps) => (

View File

@ -21,23 +21,28 @@ import Form, { FormItemProps } from 'antd/lib/form';
import { styled } from '@superset-ui/core'; import { styled } from '@superset-ui/core';
const StyledItem = styled(Form.Item)` const StyledItem = styled(Form.Item)`
.ant-form-item-label > label { ${({ theme }) => `
text-transform: uppercase; .ant-form-item-label {
font-size: ${({ theme }) => theme.typography.sizes.s}px; padding-bottom: ${theme.gridUnit}px;
color: ${({ theme }) => theme.colors.grayscale.base}; & > label {
text-transform: uppercase;
font-size: ${theme.typography.sizes.s}px;
color: ${theme.colors.grayscale.base};
&.ant-form-item-required:not(.ant-form-item-required-mark-optional) { &.ant-form-item-required:not(.ant-form-item-required-mark-optional) {
&::before { &::before {
display: none; display: none;
} }
&::after { &::after {
display: inline-block; display: inline-block;
color: ${({ theme }) => theme.colors.error.base}; color: ${theme.colors.error.base};
font-size: ${({ theme }) => theme.typography.sizes.m}px; font-size: ${theme.typography.sizes.m}px;
content: '*'; content: '*';
}
}
} }
} }
} `}
`; `;
export default function FormItem(props: FormItemProps) { export default function FormItem(props: FormItemProps) {

View File

@ -350,7 +350,7 @@ class PropertiesModal extends React.PureComponent {
<ColorSchemeControlWrapper <ColorSchemeControlWrapper
onChange={this.onColorSchemeChange} onChange={this.onColorSchemeChange}
colorScheme={values.colorScheme} colorScheme={values.colorScheme}
labelMargin={8} labelMargin={4}
/> />
</Col> </Col>
</Row> </Row>
@ -413,7 +413,7 @@ class PropertiesModal extends React.PureComponent {
<ColorSchemeControlWrapper <ColorSchemeControlWrapper
onChange={this.onColorSchemeChange} onChange={this.onColorSchemeChange}
colorScheme={values.colorScheme} colorScheme={values.colorScheme}
labelMargin={8} labelMargin={4}
/> />
</Col> </Col>
</Row> </Row>

View File

@ -18,10 +18,9 @@
*/ */
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { styled, t } from '@superset-ui/core'; import { styled, t } from '@superset-ui/core';
import { FormControl } from 'react-bootstrap';
import { Column } from 'react-table'; import { Column } from 'react-table';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { Input } from 'src/common/components';
import { import {
BOOL_FALSE_DISPLAY, BOOL_FALSE_DISPLAY,
BOOL_TRUE_DISPLAY, BOOL_TRUE_DISPLAY,
@ -73,9 +72,8 @@ export const FilterInput = ({
}) => { }) => {
const debouncedChangeHandler = debounce(onChangeHandler, SLOW_DEBOUNCE); const debouncedChangeHandler = debounce(onChangeHandler, SLOW_DEBOUNCE);
return ( return (
<FormControl <Input
placeholder={t('Search')} placeholder={t('Search')}
bsSize="sm"
onChange={(event: any) => { onChange={(event: any) => {
const filterText = event.target.value; const filterText = event.target.value;
debouncedChangeHandler(filterText); debouncedChangeHandler(filterText);

View File

@ -18,8 +18,7 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Row, Col } from 'src/common/components'; import { Row, Col, Input } from 'src/common/components';
import { FormGroup, FormControl } from 'react-bootstrap';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
import ControlHeader from '../ControlHeader'; import ControlHeader from '../ControlHeader';
@ -87,28 +86,26 @@ export default class BoundsControl extends React.Component {
return ( return (
<div> <div>
<ControlHeader {...this.props} /> <ControlHeader {...this.props} />
<FormGroup bsSize="small"> <Row gutter={16}>
<Row gutter={16}> <Col xs={12}>
<Col xs={12}> <Input
<FormControl data-test="min-bound"
data-test="min-bound" type="text"
type="text" placeholder={t('Min')}
placeholder={t('Min')} onChange={this.onMinChange}
onChange={this.onMinChange} value={this.state.minMax[0]}
value={this.state.minMax[0]} />
/> </Col>
</Col> <Col xs={12}>
<Col xs={12}> <Input
<FormControl type="text"
type="text" data-test="max-bound"
data-test="max-bound" placeholder={t('Max')}
placeholder={t('Max')} onChange={this.onMaxChange}
onChange={this.onMaxChange} value={this.state.minMax[1]}
value={this.state.minMax[1]} />
/> </Col>
</Col> </Row>
</Row>
</FormGroup>
</div> </div>
); );
} }

View File

@ -170,7 +170,7 @@ test('Should switch to tab:Custom SQL', () => {
).toBeInTheDocument(); ).toBeInTheDocument();
}); });
test('Should render "Custom SQL" tab correctly', () => { test('Should render "Custom SQL" tab correctly', async () => {
const props = createProps(); const props = createProps();
props.getCurrentTab.mockImplementation(tab => { props.getCurrentTab.mockImplementation(tab => {
props.adhocMetric.expressionType = tab; props.adhocMetric.expressionType = tab;
@ -180,5 +180,5 @@ test('Should render "Custom SQL" tab correctly', () => {
const tab = screen.getByRole('tab', { name: 'Custom SQL' }).parentElement!; const tab = screen.getByRole('tab', { name: 'Custom SQL' }).parentElement!;
userEvent.click(tab); userEvent.click(tab);
expect(screen.getByTestId('sql-editor')).toBeVisible(); expect(await screen.findByRole('textbox')).toBeInTheDocument();
}); });

View File

@ -19,13 +19,12 @@
/* eslint-disable camelcase */ /* eslint-disable camelcase */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormGroup } from 'react-bootstrap';
import Tabs from 'src/components/Tabs'; import Tabs from 'src/components/Tabs';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { NativeSelect as Select } from 'src/components/Select'; import { NativeSelect as Select } from 'src/components/Select';
import { t, styled } from '@superset-ui/core'; import { t, styled } from '@superset-ui/core';
import { FormLabel } from 'src/components/Form'; import { Form, FormItem } from 'src/components/Form';
import { SQLEditor } from 'src/components/AsyncAceEditor'; import { SQLEditor } from 'src/components/AsyncAceEditor';
import sqlKeywords from 'src/SqlLab/utils/sqlKeywords'; import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
import { noOp } from 'src/utils/common'; import { noOp } from 'src/utils/common';
@ -337,7 +336,8 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
savedMetric?.metric_name !== propsSavedMetric?.metric_name); savedMetric?.metric_name !== propsSavedMetric?.metric_name);
return ( return (
<div <Form
layout="vertical"
id="metrics-edit-popover" id="metrics-edit-popover"
data-test="metrics-edit-popover" data-test="metrics-edit-popover"
{...popoverProps} {...popoverProps}
@ -352,10 +352,7 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
allowOverflow allowOverflow
> >
<Tabs.TabPane key={SAVED_TAB_KEY} tab={t('Saved')}> <Tabs.TabPane key={SAVED_TAB_KEY} tab={t('Saved')}>
<FormGroup> <FormItem label={t('Saved metric')}>
<FormLabel>
<strong>{t('Saved metric')}</strong>
</FormLabel>
<StyledSelect <StyledSelect
{...savedSelectProps} {...savedSelectProps}
name="select-saved" name="select-saved"
@ -374,13 +371,10 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
</Select.Option> </Select.Option>
))} ))}
</StyledSelect> </StyledSelect>
</FormGroup> </FormItem>
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane key={EXPRESSION_TYPES.SIMPLE} tab={t('Simple')}> <Tabs.TabPane key={EXPRESSION_TYPES.SIMPLE} tab={t('Simple')}>
<FormGroup> <FormItem label={t('column')}>
<FormLabel>
<strong>{t('column')}</strong>
</FormLabel>
<Select <Select
{...columnSelectProps} {...columnSelectProps}
name="select-column" name="select-column"
@ -396,11 +390,8 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
</Select.Option> </Select.Option>
))} ))}
</Select> </Select>
</FormGroup> </FormItem>
<FormGroup> <FormItem label={t('aggregate')}>
<FormLabel>
<strong>{t('aggregate')}</strong>
</FormLabel>
<Select <Select
{...aggregateSelectProps} {...aggregateSelectProps}
name="select-aggregate" name="select-aggregate"
@ -412,7 +403,7 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
</Select.Option> </Select.Option>
))} ))}
</Select> </Select>
</FormGroup> </FormItem>
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane <Tabs.TabPane
key={EXPRESSION_TYPES.SQL} key={EXPRESSION_TYPES.SQL}
@ -420,24 +411,23 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
data-test="adhoc-metric-edit-tab#custom" data-test="adhoc-metric-edit-tab#custom"
> >
{this.props.datasourceType !== 'druid' ? ( {this.props.datasourceType !== 'druid' ? (
<FormGroup data-test="sql-editor"> <SQLEditor
<SQLEditor data-test="sql-editor"
showLoadingForImport showLoadingForImport
ref={this.handleAceEditorRef} ref={this.handleAceEditorRef}
keywords={keywords} keywords={keywords}
height={`${this.state.height - 80}px`} height={`${this.state.height - 80}px`}
onChange={this.onSqlExpressionChange} onChange={this.onSqlExpressionChange}
width="100%" width="100%"
showGutter={false} showGutter={false}
value={ value={
adhocMetric.sqlExpression || adhocMetric.translateToSql() adhocMetric.sqlExpression || adhocMetric.translateToSql()
} }
editorProps={{ $blockScrolling: true }} editorProps={{ $blockScrolling: true }}
enableLiveAutocompletion enableLiveAutocompletion
className="filter-sql-editor" className="adhoc-filter-sql-editor"
wrapEnabled wrapEnabled
/> />
</FormGroup>
) : ( ) : (
<div className="custom-sql-disabled-message"> <div className="custom-sql-disabled-message">
Custom SQL Metrics are not available on druid datasources Custom SQL Metrics are not available on druid datasources
@ -474,7 +464,7 @@ export default class AdhocMetricEditPopover extends React.PureComponent {
className="fa fa-expand edit-popover-resize text-muted" className="fa fa-expand edit-popover-resize text-muted"
/> />
</div> </div>
</div> </Form>
); );
} }
} }

View File

@ -18,11 +18,10 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Row, Col } from 'src/common/components'; import { Row, Col, Input } from 'src/common/components';
import { FormControl } from 'react-bootstrap';
import Popover from 'src/components/Popover'; import Popover from 'src/components/Popover';
import Select from 'src/components/Select'; import Select from 'src/components/Select';
import { t } from '@superset-ui/core'; import { t, styled } from '@superset-ui/core';
import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
import BoundsControl from '../BoundsControl'; import BoundsControl from '../BoundsControl';
@ -75,6 +74,20 @@ const colTypeOptions = [
{ value: 'avg', label: 'Period average' }, { value: 'avg', label: 'Period average' },
]; ];
const StyledRow = styled(Row)`
margin-top: ${({ theme }) => theme.gridUnit * 2}px;
`;
const StyledCol = styled(Col)`
display: flex;
align-items: center;
`;
const StyledTooltip = styled(InfoTooltipWithTrigger)`
margin-left: ${({ theme }) => theme.gridUnit}px;
color: ${({ theme }) => theme.colors.grayscale.light1};
`;
export default class TimeSeriesColumnControl extends React.Component { export default class TimeSeriesColumnControl extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -128,19 +141,15 @@ export default class TimeSeriesColumnControl extends React.Component {
formRow(label, tooltip, ttLabel, control) { formRow(label, tooltip, ttLabel, control) {
return ( return (
<Row style={{ marginTop: '5px' }}> <StyledRow>
<Col xs={24} md={10}> <StyledCol xs={24} md={10}>
{`${label} `} {label}
<InfoTooltipWithTrigger <StyledTooltip placement="top" tooltip={tooltip} label={ttLabel} />
placement="top" </StyledCol>
tooltip={tooltip} <StyledCol xs={24} md={14}>
label={ttLabel}
/>
</Col>
<Col xs={24} md={14}>
{control} {control}
</Col> </StyledCol>
</Row> </StyledRow>
); );
} }
@ -151,10 +160,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Label', 'Label',
'The column header label', 'The column header label',
'time-lag', 'time-lag',
<FormControl <Input
value={this.state.label} value={this.state.label}
onChange={this.onTextInputChange.bind(this, 'label')} onChange={this.onTextInputChange.bind(this, 'label')}
bsSize="small"
placeholder="Label" placeholder="Label"
/>, />,
)} )}
@ -162,10 +170,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Tooltip', 'Tooltip',
'Column header tooltip', 'Column header tooltip',
'col-tooltip', 'col-tooltip',
<FormControl <Input
value={this.state.tooltip} value={this.state.tooltip}
onChange={this.onTextInputChange.bind(this, 'tooltip')} onChange={this.onTextInputChange.bind(this, 'tooltip')}
bsSize="small"
placeholder="Tooltip" placeholder="Tooltip"
/>, />,
)} )}
@ -186,10 +193,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Width', 'Width',
'Width of the sparkline', 'Width of the sparkline',
'spark-width', 'spark-width',
<FormControl <Input
value={this.state.width} value={this.state.width}
onChange={this.onTextInputChange.bind(this, 'width')} onChange={this.onTextInputChange.bind(this, 'width')}
bsSize="small"
placeholder="Width" placeholder="Width"
/>, />,
)} )}
@ -198,10 +204,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Height', 'Height',
'Height of the sparkline', 'Height of the sparkline',
'spark-width', 'spark-width',
<FormControl <Input
value={this.state.height} value={this.state.height}
onChange={this.onTextInputChange.bind(this, 'height')} onChange={this.onTextInputChange.bind(this, 'height')}
bsSize="small"
placeholder="Height" placeholder="Height"
/>, />,
)} )}
@ -210,10 +215,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Time lag', 'Time lag',
'Number of periods to compare against', 'Number of periods to compare against',
'time-lag', 'time-lag',
<FormControl <Input
value={this.state.timeLag} value={this.state.timeLag}
onChange={this.onTextInputChange.bind(this, 'timeLag')} onChange={this.onTextInputChange.bind(this, 'timeLag')}
bsSize="small"
placeholder="Time Lag" placeholder="Time Lag"
/>, />,
)} )}
@ -222,10 +226,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Time ratio', 'Time ratio',
'Number of periods to ratio against', 'Number of periods to ratio against',
'time-ratio', 'time-ratio',
<FormControl <Input
value={this.state.timeRatio} value={this.state.timeRatio}
onChange={this.onTextInputChange.bind(this, 'timeRatio')} onChange={this.onTextInputChange.bind(this, 'timeRatio')}
bsSize="small"
placeholder="Time Ratio" placeholder="Time Ratio"
/>, />,
)} )}
@ -277,10 +280,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Number format', 'Number format',
'Optional d3 number format string', 'Optional d3 number format string',
'd3-format', 'd3-format',
<FormControl <Input
value={this.state.d3format} value={this.state.d3format}
onChange={this.onTextInputChange.bind(this, 'd3format')} onChange={this.onTextInputChange.bind(this, 'd3format')}
bsSize="small"
placeholder="Number format string" placeholder="Number format string"
/>, />,
)} )}
@ -289,10 +291,9 @@ export default class TimeSeriesColumnControl extends React.Component {
'Date format', 'Date format',
'Optional d3 date format string', 'Optional d3 date format string',
'date-format', 'date-format',
<FormControl <Input
value={this.state.dateFormat} value={this.state.dateFormat}
onChange={this.onTextInputChange.bind(this, 'dateFormat')} onChange={this.onTextInputChange.bind(this, 'dateFormat')}
bsSize="small"
placeholder="Date format string" placeholder="Date format string"
/>, />,
)} )}