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="popover-content"]').within(() => {
cy.get('[data-test=annotation-layer-name-header]')
.siblings()
.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('[aria-label=Name]').type(layerLabel);
cy.get('[aria-label=Formula]').type('y=1400000');
cy.get('button').contains('OK').click();
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,15 +18,14 @@
*/
import React, { useState, useEffect, useCallback } from 'react';
import Modal from 'src/components/Modal';
import { Row, Col } from 'src/common/components';
import { FormControl, FormGroup, FormControlProps } from 'react-bootstrap';
import { Row, Col, Input, TextArea } from 'src/common/components';
import Button from 'src/components/Button';
import { OptionsType } from 'react-select/src/types';
import { AsyncSelect } from 'src/components/Select';
import rison from 'rison';
import { t, SupersetClient } from '@superset-ui/core';
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';
type PropertiesModalProps = {
@ -186,37 +185,28 @@ export default function PropertiesModal({
responsive
wrapProps={{ 'data-test': 'properties-edit-modal' }}
>
<form onSubmit={onSubmit}>
<Form onFinish={onSubmit} layout="vertical">
<Row gutter={16}>
<Col xs={24} md={12}>
<h3>{t('Basic information')}</h3>
<FormGroup>
<FormLabel htmlFor="name" required>
{t('Name')}
</FormLabel>
<FormControl
<FormItem label={t('Name')} required>
<Input
name="name"
data-test="properties-modal-name-input"
type="text"
bsSize="sm"
value={name}
onChange={(
event: React.FormEvent<FormControl & FormControlProps>,
) => setName((event.currentTarget?.value as string) ?? '')}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
setName(event.target.value ?? '')
}
/>
</FormGroup>
<FormGroup>
<FormLabel htmlFor="description">{t('Description')}</FormLabel>
<FormControl
</FormItem>
<FormItem label={t('Description')}>
<TextArea
rows={3}
name="description"
type="text"
componentClass="textarea"
bsSize="sm"
value={description}
onChange={(
event: React.FormEvent<FormControl & FormControlProps>,
) =>
setDescription((event.currentTarget?.value as string) ?? '')
onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) =>
setDescription(event.target.value ?? '')
}
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.',
)}
</p>
</FormGroup>
</FormItem>
</Col>
<Col xs={24} md={12}>
<h3>{t('Configuration')}</h3>
<FormGroup>
<FormLabel htmlFor="cacheTimeout">{t('Cache timeout')}</FormLabel>
<FormControl
<FormItem label={t('Cache timeout')}>
<Input
name="cacheTimeout"
type="text"
bsSize="sm"
value={cacheTimeout}
onChange={(
event: React.FormEvent<FormControl & FormControlProps>,
) => {
const targetValue =
(event.currentTarget?.value as string) ?? '';
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const targetValue = event.target.value ?? '';
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.",
)}
</p>
</FormGroup>
</FormItem>
<h3 style={{ marginTop: '1em' }}>{t('Access')}</h3>
<FormGroup>
<FormLabel htmlFor="owners">{t('Owners')}</FormLabel>
<FormItem label={t('Owners')}>
<AsyncSelect
isMulti
name="owners"
@ -269,10 +253,10 @@ export default function PropertiesModal({
'A list of users who can alter the chart. Searchable by name or username.',
)}
</p>
</FormGroup>
</FormItem>
</Col>
</Row>
</form>
</Form>
</Modal>
);
}

View File

@ -20,7 +20,6 @@
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import { FormGroup } from 'react-bootstrap';
import AdhocFilter, {
EXPRESSION_TYPES,
@ -86,7 +85,7 @@ function setup(overrides) {
describe('AdhocFilterEditPopoverSimpleTabContent', () => {
it('renders the simple tab form', () => {
const { wrapper } = setup();
expect(wrapper.find(FormGroup)).toHaveLength(3);
expect(wrapper).toExist();
});
it('passes the new adhocFilter to onChange after onSubjectChange', () => {

View File

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

View File

@ -20,7 +20,6 @@
import React from 'react';
import sinon from 'sinon';
import { shallow } from 'enzyme';
import { FormGroup } from 'react-bootstrap';
import AdhocFilter, {
EXPRESSION_TYPES,
@ -50,7 +49,7 @@ function setup(overrides) {
describe('AdhocFilterEditPopoverSqlTabContent', () => {
it('renders the sql tab form', () => {
const { wrapper } = setup();
expect(wrapper.find(FormGroup)).toHaveLength(2);
expect(wrapper).toExist();
});
it('passes the new clause to onChange after onSqlExpressionClauseChange', () => {

View File

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

View File

@ -18,7 +18,7 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import { FormControl } from 'react-bootstrap';
import { Input } from 'src/common/components';
const propTypes = {
onChange: PropTypes.func,
@ -38,7 +38,7 @@ const defaultProps = {
export default function HiddenControl(props) {
// 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;

View File

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

View File

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

View File

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