mirror of
https://github.com/apache/superset.git
synced 2024-09-12 16:49:40 -04:00
refactor: Bootstrap to AntD - Form - iteration 4 (#14546)
This commit is contained in:
parent
3eef38f309
commit
331eb10fb9
@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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>
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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}
|
||||||
|
@ -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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user