refactor: Bootstrap to AntD - Form - iteration 4 (#14546)

This commit is contained in:
Michael S. Molina 2021-05-13 03:01:43 -03:00 committed by GitHub
parent 3eef38f309
commit 331eb10fb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 188 additions and 235 deletions

View File

@ -32,18 +32,8 @@ describe('Annotations', () => {
cy.get('[data-test=annotation_layers]').click(); cy.get('[data-test=annotation_layers]').click();
cy.get('[data-test="popover-content"]').within(() => { cy.get('[data-test="popover-content"]').within(() => {
cy.get('[data-test=annotation-layer-name-header]') cy.get('[aria-label=Name]').type(layerLabel);
.siblings() cy.get('[aria-label=Formula]').type('y=1400000');
.first()
.within(() => {
cy.get('input').type(layerLabel);
});
cy.get('[data-test=annotation-layer-value-header]')
.siblings()
.first()
.within(() => {
cy.get('input').type('y=1400000');
});
cy.get('button').contains('OK').click(); cy.get('button').contains('OK').click();
}); });

View File

@ -18,10 +18,10 @@
*/ */
/* eslint-disable no-unused-expressions */ /* eslint-disable no-unused-expressions */
import React from 'react'; import React from 'react';
import { FormControl } from 'react-bootstrap';
import sinon from 'sinon'; import sinon from 'sinon';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { TextAreaEditor } from 'src/components/AsyncAceEditor'; import { TextAreaEditor } from 'src/components/AsyncAceEditor';
import { TextArea } from 'src/common/components';
import TextAreaControl from 'src/explore/components/controls/TextAreaControl'; import TextAreaControl from 'src/explore/components/controls/TextAreaControl';
@ -38,11 +38,11 @@ describe('SelectControl', () => {
}); });
it('renders a FormControl', () => { it('renders a FormControl', () => {
expect(wrapper.find(FormControl)).toExist(); expect(wrapper.find(TextArea)).toExist();
}); });
it('calls onChange when toggled', () => { it('calls onChange when toggled', () => {
const select = wrapper.find(FormControl); const select = wrapper.find(TextArea);
select.simulate('change', { target: { value: 'x' } }); select.simulate('change', { target: { value: 'x' } });
expect(defaultProps.onChange.calledWith('x')).toBe(true); expect(defaultProps.onChange.calledWith('x')).toBe(true);
}); });
@ -51,7 +51,7 @@ describe('SelectControl', () => {
const props = { ...defaultProps }; const props = { ...defaultProps };
props.language = 'markdown'; props.language = 'markdown';
wrapper = shallow(<TextAreaControl {...props} />); wrapper = shallow(<TextAreaControl {...props} />);
expect(wrapper.find(FormControl)).not.toExist(); expect(wrapper.find(TextArea)).not.toExist();
expect(wrapper.find(TextAreaEditor)).toExist(); expect(wrapper.find(TextAreaEditor)).toExist();
}); });
}); });

View File

@ -18,10 +18,8 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormGroup, HelpBlock, FormControl } from 'react-bootstrap';
import { Tooltip } from 'src/components/Tooltip'; import { Tooltip } from 'src/components/Tooltip';
import { FormLabel } from 'src/components/Form'; import { FormItem, FormLabel } from 'src/components/Form';
import './crud.less'; import './crud.less';
const propTypes = { const propTypes = {
@ -63,19 +61,31 @@ export default class Field extends React.PureComponent {
onChange: this.onChange, onChange: this.onChange,
}); });
return ( return (
<FormGroup controlId={fieldKey}> <FormItem
<FormLabel className="m-r-5"> controlId={fieldKey}
{label || fieldKey} label={
{compact && description && ( <FormLabel className="m-r-5">
<Tooltip id="field-descr" placement="right" title={description}> {label || fieldKey}
<i className="fa fa-info-circle m-l-5" /> {compact && description && (
</Tooltip> <Tooltip id="field-descr" placement="right" title={description}>
)} <i className="fa fa-info-circle m-l-5" />
</FormLabel>{' '} </Tooltip>
)}
</FormLabel>
}
>
{hookedControl} {hookedControl}
<FormControl.Feedback /> {!compact && description && (
{!compact && description && <HelpBlock>{description}</HelpBlock>} <div
</FormGroup> css={theme => ({
color: theme.colors.grayscale.base,
marginTop: theme.gridUnit,
})}
>
{description}
</div>
)}
</FormItem>
); );
} }
} }

View File

@ -18,7 +18,7 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Form } from 'react-bootstrap'; import { Form } from 'src/components/Form';
import { recurseReactClone } from './utils'; import { recurseReactClone } from './utils';
import Field from './Field'; import Field from './Field';
@ -56,7 +56,7 @@ export default class Fieldset extends React.PureComponent {
compact: this.props.compact, compact: this.props.compact,
}); });
return ( return (
<Form componentClass="fieldset" className="CRUD"> <Form componentClass="fieldset" className="CRUD" layout="vertical">
{title && <legend>{title}</legend>} {title && <legend>{title}</legend>}
{recurseReactClone(this.props.children, Field, propExtender)} {recurseReactClone(this.props.children, Field, propExtender)}
</Form> </Form>

View File

@ -18,11 +18,11 @@
*/ */
import { t, styled } from '@superset-ui/core'; import { t, styled } from '@superset-ui/core';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FormGroup, FormControl, FormControlProps } from 'react-bootstrap'; import { Input } from 'src/common/components';
import Modal from 'src/components/Modal'; import Modal from 'src/components/Modal';
import { FormLabel } from 'src/components/Form'; import { FormLabel } from 'src/components/Form';
const StyleFormGroup = styled(FormGroup)` const StyledDiv = styled.div`
padding-top: 8px; padding-top: 8px;
width: 50%; width: 50%;
label { label {
@ -64,24 +64,21 @@ export default function DeleteModal({
title={title} title={title}
> >
<DescriptionContainer>{description}</DescriptionContainer> <DescriptionContainer>{description}</DescriptionContainer>
<StyleFormGroup> <StyledDiv>
<FormLabel htmlFor="delete"> <FormLabel htmlFor="delete">
{t('Type "%s" to confirm', t('DELETE'))} {t('Type "%s" to confirm', t('DELETE'))}
</FormLabel> </FormLabel>
<FormControl <Input
data-test="delete-modal-input" data-test="delete-modal-input"
type="text" type="text"
id="delete" id="delete"
bsSize="sm"
autoComplete="off" autoComplete="off"
onChange={( onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
event: React.FormEvent<FormControl & FormControlProps>, const targetValue = event.target.value ?? '';
) => {
const targetValue = (event.currentTarget?.value as string) ?? '';
setDisableChange(targetValue.toUpperCase() !== t('DELETE')); setDisableChange(targetValue.toUpperCase() !== t('DELETE'));
}} }}
/> />
</StyleFormGroup> </StyledDiv>
</Modal> </Modal>
); );
} }

View File

@ -23,7 +23,6 @@ import React, {
useEffect, useEffect,
useCallback, useCallback,
} from 'react'; } from 'react';
import { FormControl, FormControlProps } from 'react-bootstrap';
import Alert from 'src/components/Alert'; import Alert from 'src/components/Alert';
import { SupersetClient, t, styled } from '@superset-ui/core'; import { SupersetClient, t, styled } from '@superset-ui/core';
import TableView, { EmptyWrapperType } from 'src/components/TableView'; import TableView, { EmptyWrapperType } from 'src/components/TableView';
@ -32,9 +31,10 @@ import Button from 'src/components/Button';
import { useListViewResource } from 'src/views/CRUD/hooks'; import { useListViewResource } from 'src/views/CRUD/hooks';
import Dataset from 'src/types/Dataset'; import Dataset from 'src/types/Dataset';
import { useDebouncedEffect } from 'src/explore/exploreUtils'; import { useDebouncedEffect } from 'src/explore/exploreUtils';
import { getClientErrorObject } from '../utils/getClientErrorObject'; import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import Loading from '../components/Loading'; import Loading from 'src/components/Loading';
import withToasts from '../messageToasts/enhancers/withToasts'; import withToasts from 'src/messageToasts/enhancers/withToasts';
import { Input, AntdInput } from 'src/common/components';
const CONFIRM_WARNING_MESSAGE = t( const CONFIRM_WARNING_MESSAGE = t(
'Warning! Changing the dataset may break the chart if the metadata does not exist.', 'Warning! Changing the dataset may break the chart if the metadata does not exist.',
@ -107,7 +107,7 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
const [filter, setFilter] = useState<any>(undefined); const [filter, setFilter] = useState<any>(undefined);
const [confirmChange, setConfirmChange] = useState(false); const [confirmChange, setConfirmChange] = useState(false);
const [confirmedDataset, setConfirmedDataset] = useState<Datasource>(); const [confirmedDataset, setConfirmedDataset] = useState<Datasource>();
let searchRef = useRef<HTMLInputElement>(null); const searchRef = useRef<AntdInput>(null);
const { const {
state: { loading, resourceCollection }, state: { loading, resourceCollection },
@ -140,9 +140,7 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
useEffect(() => { useEffect(() => {
const onEnterModal = async () => { const onEnterModal = async () => {
if (searchRef && searchRef.current) { setTimeout(() => searchRef?.current?.focus(), 200);
searchRef.current.focus();
}
}; };
if (show) { if (show) {
@ -158,14 +156,8 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
show, show,
]); ]);
const setSearchRef = (ref: any) => { const changeSearch = (event: React.ChangeEvent<HTMLInputElement>) => {
searchRef = ref; const searchValue = event.target.value ?? '';
};
const changeSearch = (
event: React.FormEvent<FormControl & FormControlProps>,
) => {
const searchValue = (event.currentTarget?.value as string) ?? '';
setFilter(searchValue); setFilter(searchValue);
}; };
@ -250,24 +242,20 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
<Alert <Alert
roomBelow roomBelow
type="warning" type="warning"
css={theme => ({ marginBottom: theme.gridUnit * 4 })}
message={ message={
<> <>
<strong>{t('Warning!')}</strong> {CHANGE_WARNING_MSG} <strong>{t('Warning!')}</strong> {CHANGE_WARNING_MSG}
</> </>
} }
/> />
<div> <Input
<FormControl ref={searchRef}
inputRef={ref => { type="text"
setSearchRef(ref); value={filter}
}} placeholder={t('Search / Filter')}
type="text" onChange={changeSearch}
bsSize="sm" />
value={filter}
placeholder={t('Search / Filter')}
onChange={changeSearch}
/>
</div>
{loading && <Loading />} {loading && <Loading />}
{!loading && ( {!loading && (
<TableView <TableView

View File

@ -18,7 +18,7 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Col } from 'react-bootstrap'; import { Row, Col } from 'src/common/components';
import { Radio } from 'src/components/Radio'; import { Radio } from 'src/components/Radio';
import Card from 'src/components/Card'; import Card from 'src/components/Card';
import Alert from 'src/components/Alert'; import Alert from 'src/components/Alert';
@ -773,7 +773,7 @@ class DatasourceEditor extends React.PureComponent {
</div> </div>
)} )}
{this.state.datasourceType === DATASOURCE_TYPES.physical.key && ( {this.state.datasourceType === DATASOURCE_TYPES.physical.key && (
<Col md={6}> <Col xs={24} md={12}>
{this.state.isSqla && ( {this.state.isSqla && (
<Field <Field
fieldKey="tableSelector" fieldKey="tableSelector"
@ -1085,14 +1085,14 @@ class DatasourceEditor extends React.PureComponent {
/> />
</Tabs.TabPane> </Tabs.TabPane>
<Tabs.TabPane key={4} tab={t('Settings')}> <Tabs.TabPane key={4} tab={t('Settings')}>
<div> <Row gutter={16}>
<Col md={6}> <Col xs={24} md={12}>
<FormContainer>{this.renderSettingsFieldset()}</FormContainer> <FormContainer>{this.renderSettingsFieldset()}</FormContainer>
</Col> </Col>
<Col md={6}> <Col xs={24} md={12}>
<FormContainer>{this.renderAdvancedFieldset()}</FormContainer> <FormContainer>{this.renderAdvancedFieldset()}</FormContainer>
</Col> </Col>
</div> </Row>
</Tabs.TabPane> </Tabs.TabPane>
</StyledTableTabs> </StyledTableTabs>
</DatasourceContainer> </DatasourceContainer>

View File

@ -18,15 +18,14 @@
*/ */
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import Modal from 'src/components/Modal'; import Modal from 'src/components/Modal';
import { Row, Col } from 'src/common/components'; import { Row, Col, Input, TextArea } from 'src/common/components';
import { FormControl, FormGroup, FormControlProps } from 'react-bootstrap';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import { OptionsType } from 'react-select/src/types'; import { OptionsType } from 'react-select/src/types';
import { AsyncSelect } from 'src/components/Select'; import { AsyncSelect } from 'src/components/Select';
import rison from 'rison'; import rison from 'rison';
import { t, SupersetClient } from '@superset-ui/core'; import { t, SupersetClient } from '@superset-ui/core';
import Chart, { Slice } from 'src/types/Chart'; import Chart, { Slice } from 'src/types/Chart';
import { FormLabel } from 'src/components/Form'; import { Form, FormItem } from 'src/components/Form';
import { getClientErrorObject } from 'src/utils/getClientErrorObject'; import { getClientErrorObject } from 'src/utils/getClientErrorObject';
type PropertiesModalProps = { type PropertiesModalProps = {
@ -186,37 +185,28 @@ export default function PropertiesModal({
responsive responsive
wrapProps={{ 'data-test': 'properties-edit-modal' }} wrapProps={{ 'data-test': 'properties-edit-modal' }}
> >
<form onSubmit={onSubmit}> <Form onFinish={onSubmit} layout="vertical">
<Row gutter={16}> <Row gutter={16}>
<Col xs={24} md={12}> <Col xs={24} md={12}>
<h3>{t('Basic information')}</h3> <h3>{t('Basic information')}</h3>
<FormGroup> <FormItem label={t('Name')} required>
<FormLabel htmlFor="name" required> <Input
{t('Name')}
</FormLabel>
<FormControl
name="name" name="name"
data-test="properties-modal-name-input" data-test="properties-modal-name-input"
type="text" type="text"
bsSize="sm"
value={name} value={name}
onChange={( onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
event: React.FormEvent<FormControl & FormControlProps>, setName(event.target.value ?? '')
) => setName((event.currentTarget?.value as string) ?? '')} }
/> />
</FormGroup> </FormItem>
<FormGroup> <FormItem label={t('Description')}>
<FormLabel htmlFor="description">{t('Description')}</FormLabel> <TextArea
<FormControl rows={3}
name="description" name="description"
type="text"
componentClass="textarea"
bsSize="sm"
value={description} value={description}
onChange={( onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) =>
event: React.FormEvent<FormControl & FormControlProps>, setDescription(event.target.value ?? '')
) =>
setDescription((event.currentTarget?.value as string) ?? '')
} }
style={{ maxWidth: '100%' }} style={{ maxWidth: '100%' }}
/> />
@ -225,22 +215,17 @@ export default function PropertiesModal({
'The description can be displayed as widget headers in the dashboard view. Supports markdown.', 'The description can be displayed as widget headers in the dashboard view. Supports markdown.',
)} )}
</p> </p>
</FormGroup> </FormItem>
</Col> </Col>
<Col xs={24} md={12}> <Col xs={24} md={12}>
<h3>{t('Configuration')}</h3> <h3>{t('Configuration')}</h3>
<FormGroup> <FormItem label={t('Cache timeout')}>
<FormLabel htmlFor="cacheTimeout">{t('Cache timeout')}</FormLabel> <Input
<FormControl
name="cacheTimeout" name="cacheTimeout"
type="text" type="text"
bsSize="sm"
value={cacheTimeout} value={cacheTimeout}
onChange={( onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
event: React.FormEvent<FormControl & FormControlProps>, const targetValue = event.target.value ?? '';
) => {
const targetValue =
(event.currentTarget?.value as string) ?? '';
setCacheTimeout(targetValue.replace(/[^0-9]/, '')); setCacheTimeout(targetValue.replace(/[^0-9]/, ''));
}} }}
/> />
@ -249,10 +234,9 @@ export default function PropertiesModal({
"Duration (in seconds) of the caching timeout for this chart. Note this defaults to the dataset's timeout if undefined.", "Duration (in seconds) of the caching timeout for this chart. Note this defaults to the dataset's timeout if undefined.",
)} )}
</p> </p>
</FormGroup> </FormItem>
<h3 style={{ marginTop: '1em' }}>{t('Access')}</h3> <h3 style={{ marginTop: '1em' }}>{t('Access')}</h3>
<FormGroup> <FormItem label={t('Owners')}>
<FormLabel htmlFor="owners">{t('Owners')}</FormLabel>
<AsyncSelect <AsyncSelect
isMulti isMulti
name="owners" name="owners"
@ -269,10 +253,10 @@ export default function PropertiesModal({
'A list of users who can alter the chart. Searchable by name or username.', 'A list of users who can alter the chart. Searchable by name or username.',
)} )}
</p> </p>
</FormGroup> </FormItem>
</Col> </Col>
</Row> </Row>
</form> </Form>
</Modal> </Modal>
); );
} }

View File

@ -20,7 +20,6 @@
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 AdhocFilter, { import AdhocFilter, {
EXPRESSION_TYPES, EXPRESSION_TYPES,
@ -86,7 +85,7 @@ function setup(overrides) {
describe('AdhocFilterEditPopoverSimpleTabContent', () => { describe('AdhocFilterEditPopoverSimpleTabContent', () => {
it('renders the simple tab form', () => { it('renders the simple tab form', () => {
const { wrapper } = setup(); const { wrapper } = setup();
expect(wrapper.find(FormGroup)).toHaveLength(3); expect(wrapper).toExist();
}); });
it('passes the new adhocFilter to onChange after onSubjectChange', () => { it('passes the new adhocFilter to onChange after onSubjectChange', () => {

View File

@ -18,7 +18,6 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormGroup } from 'react-bootstrap';
import { NativeSelect as Select } from 'src/components/Select'; import { NativeSelect as Select } from 'src/components/Select';
import { Input } from 'src/common/components'; import { Input } from 'src/common/components';
import { t, SupersetClient, styled } from '@superset-ui/core'; import { t, SupersetClient, styled } from '@superset-ui/core';
@ -346,80 +345,80 @@ export default class AdhocFilterEditPopoverSimpleTabContent extends React.Compon
return ( return (
<> <>
<FormGroup className="adhoc-filter-simple-column-dropdown"> <Select
<Select css={theme => ({
{...this.selectProps} marginTop: theme.gridUnit * 4,
{...subjectSelectProps} marginBottom: theme.gridUnit * 4,
name="filter-column" })}
getPopupContainer={triggerNode => triggerNode.parentNode} {...this.selectProps}
> {...subjectSelectProps}
{columns.map(column => ( name="filter-column"
<Select.Option getPopupContainer={triggerNode => triggerNode.parentNode}
value={column.id || column.optionName} >
filterBy={ {columns.map(column => (
column.saved_metric_name || column.column_name || column.label <Select.Option
} value={column.id || column.optionName}
key={column.id || column.optionName} filterBy={
> column.saved_metric_name || column.column_name || column.label
{this.renderSubjectOptionLabel(column)} }
</Select.Option> key={column.id || column.optionName}
))}
</Select>
</FormGroup>
<FormGroup>
<Select
{...this.selectProps}
{...operatorSelectProps}
getPopupContainer={triggerNode => triggerNode.parentNode}
name="filter-operator"
>
{OPERATORS_OPTIONS.filter(op =>
this.isOperatorRelevant(op, subject),
).map(option => (
<Select.Option value={option} key={option}>
{translateOperator(option)}
</Select.Option>
))}
</Select>
</FormGroup>
<FormGroup data-test="adhoc-filter-simple-value">
{MULTI_OPERATORS.has(operator) ||
this.state.suggestions.length > 0 ? (
<SelectWithLabel
name="filter-value"
{...comparatorSelectProps}
getPopupContainer={triggerNode => triggerNode.parentNode}
onSearch={val => this.setState({ currentSuggestionSearch: val })}
onSelect={this.clearSuggestionSearch}
onBlur={this.clearSuggestionSearch}
> >
{this.state.suggestions.map(suggestion => ( {this.renderSubjectOptionLabel(column)}
<Select.Option value={suggestion} key={suggestion}> </Select.Option>
{suggestion} ))}
</Select.Option> </Select>
))} <Select
css={theme => ({ marginBottom: theme.gridUnit * 4 })}
{...this.selectProps}
{...operatorSelectProps}
getPopupContainer={triggerNode => triggerNode.parentNode}
name="filter-operator"
>
{OPERATORS_OPTIONS.filter(op =>
this.isOperatorRelevant(op, subject),
).map(option => (
<Select.Option value={option} key={option}>
{translateOperator(option)}
</Select.Option>
))}
</Select>
{MULTI_OPERATORS.has(operator) || this.state.suggestions.length > 0 ? (
<SelectWithLabel
data-test="adhoc-filter-simple-value"
name="filter-value"
{...comparatorSelectProps}
getPopupContainer={triggerNode => triggerNode.parentNode}
onSearch={val => this.setState({ currentSuggestionSearch: val })}
onSelect={this.clearSuggestionSearch}
onBlur={this.clearSuggestionSearch}
>
{this.state.suggestions.map(suggestion => (
<Select.Option value={suggestion} key={suggestion}>
{suggestion}
</Select.Option>
))}
{/* enable selecting an option not included in suggestions */} {/* enable selecting an option not included in suggestions */}
{currentSuggestionSearch && {currentSuggestionSearch &&
!this.state.suggestions.some( !this.state.suggestions.some(
suggestion => suggestion === currentSuggestionSearch, suggestion => suggestion === currentSuggestionSearch,
) && ( ) && (
<Select.Option value={currentSuggestionSearch}> <Select.Option value={currentSuggestionSearch}>
{currentSuggestionSearch} {currentSuggestionSearch}
</Select.Option> </Select.Option>
)} )}
</SelectWithLabel> </SelectWithLabel>
) : ( ) : (
<Input <Input
name="filter-value" data-test="adhoc-filter-simple-value"
ref={ref => this.focusComparator(ref, focusComparator)} name="filter-value"
onChange={this.onInputComparatorChange} ref={ref => this.focusComparator(ref, focusComparator)}
value={comparator} onChange={this.onInputComparatorChange}
placeholder={t('Filter value (case sensitive)')} value={comparator}
disabled={DISABLE_INPUT_OPERATORS.includes(operator)} placeholder={t('Filter value (case sensitive)')}
/> disabled={DISABLE_INPUT_OPERATORS.includes(operator)}
)} />
</FormGroup> )}
</> </>
); );
} }

View File

@ -20,7 +20,6 @@
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 AdhocFilter, { import AdhocFilter, {
EXPRESSION_TYPES, EXPRESSION_TYPES,
@ -50,7 +49,7 @@ function setup(overrides) {
describe('AdhocFilterEditPopoverSqlTabContent', () => { describe('AdhocFilterEditPopoverSqlTabContent', () => {
it('renders the sql tab form', () => { it('renders the sql tab form', () => {
const { wrapper } = setup(); const { wrapper } = setup();
expect(wrapper.find(FormGroup)).toHaveLength(2); expect(wrapper).toExist();
}); });
it('passes the new clause to onChange after onSqlExpressionClauseChange', () => { it('passes the new clause to onChange after onSqlExpressionClauseChange', () => {

View File

@ -18,7 +18,6 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormGroup } from 'react-bootstrap';
import { NativeSelect as Select } from 'src/components/Select'; import { NativeSelect as Select } from 'src/components/Select';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
import { SQLEditor } from 'src/components/AsyncAceEditor'; import { SQLEditor } from 'src/components/AsyncAceEditor';
@ -115,7 +114,7 @@ export default class AdhocFilterEditPopoverSqlTabContent extends React.Component
return ( return (
<span> <span>
<FormGroup className="filter-edit-clause-section"> <div className="filter-edit-clause-section">
<Select <Select
{...this.selectProps} {...this.selectProps}
{...clauseSelectProps} {...clauseSelectProps}
@ -133,8 +132,8 @@ export default class AdhocFilterEditPopoverSqlTabContent extends React.Component
<br /> <br />
<strong>HAVING</strong> {t('Filters by metrics')} <strong>HAVING</strong> {t('Filters by metrics')}
</span> </span>
</FormGroup> </div>
<FormGroup> <div css={theme => ({ marginTop: theme.gridUnit * 4 })}>
<SQLEditor <SQLEditor
ref={this.handleAceEditorRef} ref={this.handleAceEditorRef}
keywords={keywords} keywords={keywords}
@ -148,7 +147,7 @@ export default class AdhocFilterEditPopoverSqlTabContent extends React.Component
className="filter-sql-editor" className="filter-sql-editor"
wrapEnabled wrapEnabled
/> />
</FormGroup> </div>
</span> </span>
); );
} }

View File

@ -18,7 +18,7 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormControl } from 'react-bootstrap'; import { Input } from 'src/common/components';
const propTypes = { const propTypes = {
onChange: PropTypes.func, onChange: PropTypes.func,
@ -38,7 +38,7 @@ const defaultProps = {
export default function HiddenControl(props) { export default function HiddenControl(props) {
// This wouldn't be necessary but might as well // This wouldn't be necessary but might as well
return <FormControl type="hidden" value={props.value} />; return <Input type="hidden" value={props.value} />;
} }
HiddenControl.propTypes = propTypes; HiddenControl.propTypes = propTypes;

View File

@ -19,7 +19,7 @@
import React from 'react'; import React from 'react';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormControl } from 'react-bootstrap'; import { Input } from 'src/common/components';
import { Tooltip } from 'src/components/Tooltip'; import { Tooltip } from 'src/components/Tooltip';
const propTypes = { const propTypes = {
@ -79,7 +79,7 @@ export default class AdhocMetricEditPopoverTitle extends React.Component {
} }
return this.state.isEditMode ? ( return this.state.isEditMode ? (
<FormControl <Input
className="metric-edit-popover-label-input" className="metric-edit-popover-label-input"
type="text" type="text"
placeholder={title.label} placeholder={title.label}

View File

@ -18,7 +18,7 @@
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormGroup, FormControl } from 'react-bootstrap'; import { TextArea } from 'src/common/components';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { t } from '@superset-ui/core'; import { t } from '@superset-ui/core';
@ -99,16 +99,13 @@ export default class TextAreaControl extends React.Component {
); );
} }
return ( return (
<FormGroup controlId="formControlsTextarea"> <TextArea
<FormControl placeholder={t('textarea')}
componentClass="textarea" onChange={this.onControlChange.bind(this)}
placeholder={t('textarea')} value={value}
onChange={this.onControlChange.bind(this)} disabled={this.props.readOnly}
value={value} style={{ height: this.props.height }}
disabled={this.props.readOnly} />
style={{ height: this.props.height }}
/>
</FormGroup>
); );
} }

View File

@ -17,11 +17,11 @@
* under the License. * under the License.
*/ */
import React from 'react'; import React from 'react';
import { FormGroup, FormControl, FormControlProps } from 'react-bootstrap';
import { legacyValidateNumber, legacyValidateInteger } from '@superset-ui/core'; import { legacyValidateNumber, legacyValidateInteger } from '@superset-ui/core';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { FAST_DEBOUNCE } from 'src/constants'; import { FAST_DEBOUNCE } from 'src/constants';
import ControlHeader from 'src/explore/components/ControlHeader'; import ControlHeader from 'src/explore/components/ControlHeader';
import { Input } from 'src/common/components';
type InputValueType = string | number; type InputValueType = string | number;
@ -39,13 +39,9 @@ export interface TextControlProps<T extends InputValueType = InputValueType> {
} }
export interface TextControlState { export interface TextControlState {
controlId: string;
value: string; value: string;
} }
const generateControlId = (controlId?: string) =>
`formInlineName_${controlId ?? (Math.random() * 1000000).toFixed()}`;
const safeStringify = (value?: InputValueType | null) => const safeStringify = (value?: InputValueType | null) =>
value == null ? '' : String(value); value == null ? '' : String(value);
@ -58,9 +54,6 @@ export default class TextControl<
super(props); super(props);
this.initialValue = props.value; this.initialValue = props.value;
this.state = { this.state = {
// if there's no control id provided, generate a random
// number to prevent rendering elements with same ids
controlId: generateControlId(props.controlId),
value: safeStringify(this.initialValue), value: safeStringify(this.initialValue),
}; };
} }
@ -94,8 +87,8 @@ export default class TextControl<
this.onChange(inputValue); this.onChange(inputValue);
}, FAST_DEBOUNCE); }, FAST_DEBOUNCE);
onChangeWrapper: FormControlProps['onChange'] = event => { onChangeWrapper = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target as HTMLInputElement; const { value } = event.target;
this.setState({ value }, () => { this.setState({ value }, () => {
this.debouncedOnChange(value); this.debouncedOnChange(value);
}); });
@ -110,18 +103,16 @@ export default class TextControl<
return ( return (
<div> <div>
<ControlHeader {...this.props} /> <ControlHeader {...this.props} />
<FormGroup controlId={this.state.controlId}> <Input
<FormControl type="text"
type="text" data-test="inline-name"
data-test="inline-name" placeholder={this.props.placeholder}
placeholder={this.props.placeholder} onChange={this.onChangeWrapper}
onChange={this.onChangeWrapper} onFocus={this.props.onFocus}
onFocus={this.props.onFocus} value={value}
value={value} disabled={this.props.disabled}
disabled={this.props.disabled} aria-label={this.props.label}
aria-label={this.props.label} />
/>
</FormGroup>
</div> </div>
); );
}; };