Merge pull request #2859 from influxdata/fix/enable-save
Enable Mapping Save button when Validpull/2976/head
commit
49b9d44e5a
|
@ -15,6 +15,9 @@
|
|||
|
||||
## v1.4.2.2 [2018-03-07]
|
||||
### Bug Fixes
|
||||
1. [#2866](https://github.com/influxdata/chronograf/pull/2866): Change hover text on delete mappings confirmation button to 'Delete'
|
||||
1. [#2911](https://github.com/influxdata/chronograf/pull/2911): Fix Heroku OAuth
|
||||
1. [#2859](https://github.com/influxdata/chronograf/pull/2859): Enable Mappings save button when valid
|
||||
1. [#2933](https://github.com/influxdata/chronograf/pull/2933): Include url in Kapacitor connection creation requests
|
||||
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ class OrganizationsTableRow extends Component {
|
|||
<InputClickToEdit
|
||||
value={organization.name}
|
||||
wrapperClass="fancytable--td orgs-table--name"
|
||||
onUpdate={this.handleUpdateOrgName}
|
||||
onBlur={this.handleUpdateOrgName}
|
||||
/>
|
||||
<div className={defaultRoleClassName}>
|
||||
<Dropdown
|
||||
|
|
|
@ -80,14 +80,14 @@ class ProvidersTableRow extends Component {
|
|||
<InputClickToEdit
|
||||
value={provider}
|
||||
wrapperClass="fancytable--td provider--provider"
|
||||
onUpdate={this.handleChangeProvider}
|
||||
onBlur={this.handleChangeProvider}
|
||||
disabled={isDefaultMapping}
|
||||
tabIndex={rowIndex}
|
||||
/>
|
||||
<InputClickToEdit
|
||||
value={providerOrganization}
|
||||
wrapperClass="fancytable--td provider--providerorg"
|
||||
onUpdate={this.handleChangeProviderOrg}
|
||||
onBlur={this.handleChangeProviderOrg}
|
||||
disabled={isDefaultMapping}
|
||||
tabIndex={rowIndex}
|
||||
/>
|
||||
|
|
|
@ -1,10 +1,34 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import React, {PureComponent} from 'react'
|
||||
|
||||
import ConfirmButtons from 'shared/components/ConfirmButtons'
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
import InputClickToEdit from 'shared/components/InputClickToEdit'
|
||||
import ConfirmButtons from 'src/shared/components/ConfirmButtons'
|
||||
import Dropdown from 'src/shared/components/Dropdown'
|
||||
import InputClickToEdit from 'src/shared/components/InputClickToEdit'
|
||||
|
||||
class ProvidersTableRowNew extends Component {
|
||||
type Organization = {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
type Scheme = {
|
||||
text: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
organizations: Organization[]
|
||||
schemes?: Scheme[]
|
||||
rowIndex?: number
|
||||
onCreate: (state: State) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
scheme: string
|
||||
provider: string
|
||||
providerOrganization: string
|
||||
organizationId: string
|
||||
}
|
||||
|
||||
class ProvidersTableRowNew extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
|
@ -14,25 +38,31 @@ class ProvidersTableRowNew extends Component {
|
|||
providerOrganization: null,
|
||||
organizationId: 'default',
|
||||
}
|
||||
|
||||
this.handleChooseScheme = this.handleChooseScheme.bind(this)
|
||||
this.handleChangeProvider = this.handleChangeProvider.bind(this)
|
||||
this.handleChangeProviderOrg = this.handleChangeProviderOrg.bind(this)
|
||||
this.handleChooseOrganization = this.handleChooseOrganization.bind(this)
|
||||
this.handleSaveNewMapping = this.handleSaveNewMapping.bind(this)
|
||||
}
|
||||
|
||||
handleChooseScheme = scheme => {
|
||||
handleChooseScheme(scheme: Scheme) {
|
||||
this.setState({scheme: scheme.text})
|
||||
}
|
||||
|
||||
handleChangeProvider = provider => {
|
||||
handleChangeProvider(provider: string) {
|
||||
this.setState({provider})
|
||||
}
|
||||
|
||||
handleChangeProviderOrg = providerOrganization => {
|
||||
handleChangeProviderOrg(providerOrganization: string) {
|
||||
this.setState({providerOrganization})
|
||||
}
|
||||
|
||||
handleChooseOrganization = org => {
|
||||
handleChooseOrganization(org: Organization) {
|
||||
this.setState({organizationId: org.id})
|
||||
}
|
||||
|
||||
handleSaveNewMapping = () => {
|
||||
handleSaveNewMapping() {
|
||||
const {onCreate} = this.props
|
||||
onCreate(this.state)
|
||||
}
|
||||
|
@ -62,14 +92,16 @@ class ProvidersTableRowNew extends Component {
|
|||
<InputClickToEdit
|
||||
value={provider}
|
||||
wrapperClass="fancytable--td provider--provider"
|
||||
onUpdate={this.handleChangeProvider}
|
||||
onChange={this.handleChangeProvider}
|
||||
onBlur={this.handleChangeProvider}
|
||||
tabIndex={rowIndex}
|
||||
placeholder="google"
|
||||
/>
|
||||
<InputClickToEdit
|
||||
value={providerOrganization}
|
||||
wrapperClass="fancytable--td provider--providerorg"
|
||||
onUpdate={this.handleChangeProviderOrg}
|
||||
onChange={this.handleChangeProviderOrg}
|
||||
onBlur={this.handleChangeProviderOrg}
|
||||
tabIndex={rowIndex}
|
||||
placeholder="*"
|
||||
/>
|
||||
|
@ -94,23 +126,4 @@ class ProvidersTableRowNew extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, number, shape, string} = PropTypes
|
||||
|
||||
ProvidersTableRowNew.propTypes = {
|
||||
organizations: arrayOf(
|
||||
shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
schemes: arrayOf(
|
||||
shape({
|
||||
text: string.isRequired,
|
||||
})
|
||||
),
|
||||
rowIndex: number,
|
||||
onCreate: func.isRequired,
|
||||
onCancel: func.isRequired,
|
||||
}
|
||||
|
||||
export default ProvidersTableRowNew
|
|
@ -15,7 +15,7 @@ const GraphOptionsCustomizableColumn = ({
|
|||
<InputClickToEdit
|
||||
value={newColumnName}
|
||||
wrapperClass="column-controls-input"
|
||||
onUpdate={onColumnRename}
|
||||
onBlur={onColumnRename}
|
||||
placeholder="Rename..."
|
||||
appearAsNormalInput={true}
|
||||
/>
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
class InputClickToEdit extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isEditing: null,
|
||||
value: this.props.value,
|
||||
}
|
||||
}
|
||||
|
||||
handleCancel = () => {
|
||||
this.setState({
|
||||
isEditing: false,
|
||||
value: this.props.value,
|
||||
})
|
||||
}
|
||||
|
||||
handleInputClick = () => {
|
||||
this.setState({isEditing: true})
|
||||
}
|
||||
|
||||
handleInputBlur = e => {
|
||||
const {onUpdate, value} = this.props
|
||||
|
||||
if (value !== e.target.value) {
|
||||
onUpdate(e.target.value)
|
||||
}
|
||||
|
||||
this.setState({isEditing: false, value: e.target.value})
|
||||
}
|
||||
|
||||
handleKeyDown = e => {
|
||||
if (e.key === 'Enter') {
|
||||
this.handleInputBlur(e)
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
this.handleCancel()
|
||||
}
|
||||
}
|
||||
|
||||
handleFocus = e => {
|
||||
e.target.select()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {isEditing, value} = this.state
|
||||
const {
|
||||
wrapperClass: wrapper,
|
||||
disabled,
|
||||
tabIndex,
|
||||
placeholder,
|
||||
appearAsNormalInput,
|
||||
} = this.props
|
||||
|
||||
const wrapperClass = `${wrapper}${appearAsNormalInput
|
||||
? ' input-cte__normal'
|
||||
: ''}`
|
||||
const defaultStyle = value ? 'input-cte' : 'input-cte__empty'
|
||||
|
||||
return disabled
|
||||
? <div className={wrapperClass}>
|
||||
<div className="input-cte__disabled">
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
: <div className={wrapperClass}>
|
||||
{isEditing
|
||||
? <input
|
||||
type="text"
|
||||
className="form-control input-sm provider--input"
|
||||
defaultValue={value}
|
||||
onBlur={this.handleInputBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
autoFocus={true}
|
||||
onFocus={this.handleFocus}
|
||||
ref={r => (this.inputRef = r)}
|
||||
tabIndex={tabIndex}
|
||||
spellCheck={false}
|
||||
/>
|
||||
: <div
|
||||
className={defaultStyle}
|
||||
onClick={this.handleInputClick}
|
||||
onFocus={this.handleInputClick}
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
{value || placeholder}
|
||||
{appearAsNormalInput || <span className="icon pencil" />}
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
const {func, bool, number, string} = PropTypes
|
||||
|
||||
InputClickToEdit.propTypes = {
|
||||
wrapperClass: string.isRequired,
|
||||
value: string,
|
||||
onUpdate: func.isRequired,
|
||||
disabled: bool,
|
||||
tabIndex: number,
|
||||
placeholder: string,
|
||||
appearAsNormalInput: bool,
|
||||
}
|
||||
|
||||
export default InputClickToEdit
|
|
@ -0,0 +1,145 @@
|
|||
import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react'
|
||||
|
||||
interface Props {
|
||||
wrapperClass: string
|
||||
value?: string
|
||||
onChange?: (value: string) => void
|
||||
onBlur: (value: string) => void
|
||||
disabled?: boolean
|
||||
tabIndex?: number
|
||||
placeholder?: string
|
||||
appearAsNormalInput?: boolean
|
||||
}
|
||||
|
||||
interface State {
|
||||
isEditing: boolean
|
||||
initialValue: string
|
||||
}
|
||||
|
||||
class InputClickToEdit extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isEditing: false,
|
||||
initialValue: this.props.value,
|
||||
}
|
||||
|
||||
this.handleCancel = this.handleCancel.bind(this)
|
||||
this.handleInputClick = this.handleInputClick.bind(this)
|
||||
this.handleInputBlur = this.handleInputBlur.bind(this)
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this)
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
this.handleFocus = this.handleFocus.bind(this)
|
||||
}
|
||||
|
||||
public static defaultProps: Partial<Props> = {
|
||||
tabIndex: 0,
|
||||
}
|
||||
|
||||
handleCancel() {
|
||||
const {onChange} = this.props
|
||||
const {initialValue} = this.state
|
||||
this.setState({
|
||||
isEditing: false,
|
||||
})
|
||||
if (onChange) {
|
||||
onChange(initialValue)
|
||||
}
|
||||
}
|
||||
|
||||
handleInputClick() {
|
||||
this.setState({isEditing: true})
|
||||
}
|
||||
|
||||
handleInputBlur(e: ChangeEvent<HTMLInputElement>) {
|
||||
const {onBlur, value} = this.props
|
||||
if (value !== e.target.value) {
|
||||
onBlur(e.target.value)
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isEditing: false,
|
||||
initialValue: e.target.value,
|
||||
})
|
||||
}
|
||||
|
||||
handleKeyDown(e: KeyboardEvent<HTMLInputElement>) {
|
||||
const {onBlur, value} = this.props
|
||||
if (e.key === 'Enter') {
|
||||
if (value !== e.currentTarget.value) {
|
||||
onBlur(e.currentTarget.value)
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isEditing: false,
|
||||
initialValue: e.currentTarget.value,
|
||||
})
|
||||
}
|
||||
if (e.key === 'Escape') {
|
||||
this.handleCancel()
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e: ChangeEvent<HTMLInputElement>) {
|
||||
const {onChange} = this.props
|
||||
if (onChange) {
|
||||
onChange(e.target.value)
|
||||
}
|
||||
}
|
||||
|
||||
handleFocus(e: ChangeEvent<HTMLInputElement>) {
|
||||
e.target.select()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {isEditing} = this.state
|
||||
const {
|
||||
wrapperClass: wrapper,
|
||||
disabled,
|
||||
tabIndex,
|
||||
placeholder,
|
||||
value,
|
||||
appearAsNormalInput,
|
||||
} = this.props
|
||||
|
||||
const wrapperClass = `${wrapper}${appearAsNormalInput
|
||||
? ' input-cte__normal'
|
||||
: ''}`
|
||||
const defaultStyle = value ? 'input-cte' : 'input-cte__empty'
|
||||
|
||||
return disabled
|
||||
? <div className={wrapperClass}>
|
||||
<div data-test="disabled" className="input-cte__disabled">
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
: <div className={wrapperClass}>
|
||||
{isEditing
|
||||
? <input
|
||||
type="text"
|
||||
className="form-control input-sm provider--input"
|
||||
defaultValue={value}
|
||||
onBlur={this.handleInputBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onChange={this.handleChange}
|
||||
autoFocus={true}
|
||||
onFocus={this.handleFocus}
|
||||
tabIndex={tabIndex}
|
||||
spellCheck={false}
|
||||
/>
|
||||
: <div
|
||||
className={defaultStyle}
|
||||
onClick={this.handleInputClick}
|
||||
onFocus={this.handleInputClick}
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
{value || placeholder}
|
||||
{appearAsNormalInput ||
|
||||
<span data-test="icon" className="icon pencil" />}
|
||||
</div>}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
export default InputClickToEdit
|
|
@ -0,0 +1,91 @@
|
|||
import React from 'react'
|
||||
import InputClickToEdit from 'src/shared/components/InputClickToEdit'
|
||||
|
||||
import {shallow} from 'enzyme'
|
||||
|
||||
const setup = (override = {}) => {
|
||||
const props = {
|
||||
wrapperClass: '',
|
||||
value: '',
|
||||
onChange: () => {},
|
||||
onBlur: () => {},
|
||||
disabled: false,
|
||||
tabIndex: 0,
|
||||
placeholder: '',
|
||||
appearAsNormalInput: false,
|
||||
...override,
|
||||
}
|
||||
|
||||
const defaultState = {
|
||||
isEditing: false,
|
||||
initialValue: '',
|
||||
}
|
||||
|
||||
const inputClickToEdit = shallow(<InputClickToEdit {...props} />)
|
||||
return {
|
||||
props,
|
||||
defaultState,
|
||||
inputClickToEdit,
|
||||
}
|
||||
}
|
||||
|
||||
describe('Components.Shared.InputClickToEdit', () => {
|
||||
describe('rendering', () => {
|
||||
it('does not display input by default', () => {
|
||||
const {inputClickToEdit} = setup()
|
||||
const initialDiv = inputClickToEdit.children().find('div')
|
||||
const inputField = inputClickToEdit.children().find('input')
|
||||
const disabledDiv = inputClickToEdit
|
||||
.children()
|
||||
.find({'data-test': 'disabled'})
|
||||
|
||||
expect(initialDiv.exists()).toBe(true)
|
||||
expect(inputField.exists()).toBe(false)
|
||||
expect(disabledDiv.exists()).toBe(false)
|
||||
})
|
||||
|
||||
describe('if disabled passed in as props is true', () => {
|
||||
it('should show the disabled div', () => {
|
||||
const disabled = true
|
||||
const {inputClickToEdit} = setup({disabled})
|
||||
const inputField = inputClickToEdit.children().find('input')
|
||||
const disabledDiv = inputClickToEdit
|
||||
.children()
|
||||
.find({'data-test': 'disabled'})
|
||||
|
||||
expect(inputField.exists()).toBe(false)
|
||||
expect(disabledDiv.exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('should not have an icon', () => {
|
||||
const disabled = true
|
||||
const {inputClickToEdit} = setup({disabled})
|
||||
const disabledDiv = inputClickToEdit
|
||||
.children()
|
||||
.find({'data-test': 'disabled'})
|
||||
const icon = disabledDiv.children().find({
|
||||
'data-test': 'icon',
|
||||
})
|
||||
|
||||
expect(icon.exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('user interaction', () => {
|
||||
describe('when clicking component', () => {
|
||||
it('should render input field', () => {
|
||||
const {inputClickToEdit} = setup()
|
||||
const initialDiv = inputClickToEdit.children().find('div')
|
||||
initialDiv.simulate('click')
|
||||
const divAfterClick = inputClickToEdit.children().find('div')
|
||||
const inputField = inputClickToEdit.children().find('input')
|
||||
const isEditing = inputClickToEdit.state('isEditing')
|
||||
|
||||
expect(isEditing).toBe(true)
|
||||
expect(divAfterClick.exists()).toBe(false)
|
||||
expect(inputField.exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,102 @@
|
|||
import React from 'react'
|
||||
import ProvidersTableRowNew from 'src/admin/components/chronograf/ProvidersTableRowNew'
|
||||
import ConfirmButtons from 'src/shared/components/ConfirmButtons'
|
||||
import InputClickToEdit from 'src/shared/components/InputClickToEdit'
|
||||
|
||||
import {shallow} from 'enzyme'
|
||||
|
||||
const setup = (override = {}) => {
|
||||
const props = {
|
||||
organizations: [],
|
||||
schemes: [],
|
||||
rowIndex: 0,
|
||||
onCreate: () => {},
|
||||
onCancel: jest.fn(),
|
||||
...override,
|
||||
}
|
||||
|
||||
const defaultState = {
|
||||
scheme: '*',
|
||||
provider: null,
|
||||
providerOrganization: null,
|
||||
organizationId: 'default',
|
||||
}
|
||||
|
||||
const row = shallow(<ProvidersTableRowNew {...props} />)
|
||||
return {
|
||||
props,
|
||||
defaultState,
|
||||
row,
|
||||
}
|
||||
}
|
||||
|
||||
describe('Components.Shared.ProvidersTableRowNew', () => {
|
||||
describe('user interaction', () => {
|
||||
describe('provider and providerOrganization in state are null', () => {
|
||||
it('should have a disabled confirm button', () => {
|
||||
const organizations = [{id: 'default', name: 'default'}]
|
||||
const {row} = setup({organizations})
|
||||
|
||||
const confirmButtons = row.find(ConfirmButtons)
|
||||
const confirmButtonsDisabled = confirmButtons.prop('isDisabled')
|
||||
|
||||
expect(confirmButtonsDisabled).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('provider has a value in state and providerOrganization is null', () => {
|
||||
it('should have a disabled confirm button', () => {
|
||||
const organizations = [{id: 'default', name: 'default'}]
|
||||
const {row} = setup({organizations})
|
||||
const inputClickToEdits = row.find(InputClickToEdit)
|
||||
|
||||
const providerInput = inputClickToEdits.first()
|
||||
providerInput.simulate('click')
|
||||
providerInput.simulate('change', '*')
|
||||
|
||||
const confirmButtons = row.find(ConfirmButtons)
|
||||
const confirmButtonsDisabled = confirmButtons.prop('isDisabled')
|
||||
|
||||
expect(confirmButtonsDisabled).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('providerOrganization has a value in state and provider is null', () => {
|
||||
it('should have a disabled confirm button', () => {
|
||||
const organizations = [{id: 'default', name: 'default'}]
|
||||
const {row} = setup({organizations})
|
||||
const inputClickToEdits = row.find(InputClickToEdit)
|
||||
|
||||
const providerOrganizationInput = inputClickToEdits.last()
|
||||
providerOrganizationInput.simulate('click')
|
||||
providerOrganizationInput.simulate('change', '*')
|
||||
|
||||
const confirmButtons = row.find(ConfirmButtons)
|
||||
const confirmButtonsDisabled = confirmButtons.prop('isDisabled')
|
||||
|
||||
expect(confirmButtonsDisabled).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('provider and providerOrganization have values in state', () => {
|
||||
it('should not have a disabled confirm button', () => {
|
||||
const organizations = [{id: 'default', name: 'default'}]
|
||||
const {row} = setup({organizations})
|
||||
const inputClickToEdits = row.find(InputClickToEdit)
|
||||
|
||||
const providerInput = inputClickToEdits.first()
|
||||
providerInput.simulate('click')
|
||||
providerInput.simulate('change', '*')
|
||||
|
||||
const providerOrganizationInput = inputClickToEdits.last()
|
||||
providerOrganizationInput.simulate('click')
|
||||
providerOrganizationInput.simulate('change', '*')
|
||||
|
||||
const confirmButtons = row.find(ConfirmButtons)
|
||||
const confirmButtonsDisabled = confirmButtons.prop('isDisabled')
|
||||
|
||||
expect(confirmButtonsDisabled).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue