2020-12-26 18:04:23 -08:00
2020-12-28 01:11:26 -08:00
import React , { useState , useContext } from 'react' ;
2021-01-03 01:54:04 -08:00
import { Button , Form , Input , InputNumber } from 'antd' ;
2020-12-28 01:11:26 -08:00
import { FormItemProps } from 'antd/es/form' ;
2020-12-26 18:04:23 -08:00
2021-01-03 01:10:38 -08:00
import { TEXTFIELD_DEFAULTS , TEXT_MAXLENGTH , RESET_TIMEOUT , postConfigUpdateToAPI } from './constants' ;
2020-12-26 18:04:23 -08:00
2020-12-28 01:11:26 -08:00
import { TextFieldProps } from '../../../types/config-section' ;
import { ServerStatusContext } from '../../../utils/server-status-context' ;
2021-01-03 01:54:04 -08:00
import InfoTip from '../info-tip' ;
2020-12-26 19:44:09 -08:00
export const TEXTFIELD_TYPE_TEXT = 'default' ;
2020-12-28 01:11:26 -08:00
export const TEXTFIELD_TYPE_PASSWORD = 'password' ; // Input.Password
2020-12-26 19:44:09 -08:00
export const TEXTFIELD_TYPE_NUMBER = 'numeric' ;
2020-12-29 02:51:56 -08:00
export const TEXTFIELD_TYPE_TEXTAREA = 'textarea' ;
2021-01-03 04:03:18 -08:00
export const TEXTFIELD_TYPE_URL = 'url' ;
2020-12-29 02:51:56 -08:00
2020-12-26 19:44:09 -08:00
export default function TextField ( props : TextFieldProps ) {
2020-12-28 01:11:26 -08:00
const [ submitStatus , setSubmitStatus ] = useState < FormItemProps [ 'validateStatus' ] > ( '' ) ;
const [ submitStatusMessage , setSubmitStatusMessage ] = useState ( '' ) ;
2020-12-29 02:51:56 -08:00
const [ hasChanged , setHasChanged ] = useState ( false ) ;
const [ fieldValueForSubmit , setFieldValueForSubmit ] = useState ( '' ) ;
2020-12-28 01:11:26 -08:00
2020-12-29 02:51:56 -08:00
let resetTimer = null ;
2020-12-28 01:11:26 -08:00
2020-12-29 02:51:56 -08:00
const serverStatusData = useContext ( ServerStatusContext ) ;
2021-01-03 01:54:04 -08:00
const { setFieldInConfigState } = serverStatusData || { } ;
2020-12-29 02:51:56 -08:00
2020-12-26 19:44:09 -08:00
const {
2021-01-03 00:26:26 -08:00
configPath = '' ,
disabled = false ,
2020-12-28 01:11:26 -08:00
fieldName ,
2021-01-09 13:12:14 -08:00
handleResetValue = ( ) = > { } ,
2021-01-03 00:26:26 -08:00
initialValues = { } ,
2021-01-19 17:26:28 -08:00
placeholder ,
2021-01-03 00:26:26 -08:00
onSubmit ,
2021-01-03 04:10:08 -08:00
onBlur ,
onChange ,
2021-01-03 00:26:26 -08:00
type ,
2020-12-26 19:44:09 -08:00
} = props ;
2020-12-28 01:11:26 -08:00
2021-01-03 00:26:26 -08:00
// Keep track of what the initial value is
// Note: we're not using `initialValue` as a prop, because we expect this component to be controlled by a parent Ant <Form> which is doing a form.setFieldsValue() upstream.
2020-12-29 02:51:56 -08:00
const initialValue = initialValues [ fieldName ] || '' ;
2021-01-03 00:26:26 -08:00
// Get other static info we know about this field.
const defaultDetails = TEXTFIELD_DEFAULTS [ configPath ] || TEXTFIELD_DEFAULTS ;
2020-12-28 01:11:26 -08:00
const {
apiPath = '' ,
maxLength = TEXT_MAXLENGTH ,
label = '' ,
tip = '' ,
2021-01-03 00:26:26 -08:00
required = false ,
} = defaultDetails [ fieldName ] || { } ;
2020-12-28 01:11:26 -08:00
2021-01-03 00:26:26 -08:00
// Clear out any validation states and messaging
2020-12-29 02:51:56 -08:00
const resetStates = ( ) = > {
setSubmitStatus ( '' ) ;
setHasChanged ( false ) ;
clearTimeout ( resetTimer ) ;
resetTimer = null ;
2021-01-03 23:32:47 -08:00
} ;
2020-12-29 02:51:56 -08:00
2021-01-03 00:26:26 -08:00
// if field is required but value is empty, or equals initial value, then don't show submit/update button. otherwise clear out any result messaging and display button.
const handleChange = ( e : any ) = > {
2020-12-30 18:07:15 -08:00
const val = type === TEXTFIELD_TYPE_NUMBER ? e : e.target.value ;
2021-01-03 04:03:18 -08:00
// https://developer.mozilla.org/en-US/docs/Web/API/ValidityState
2021-01-09 13:12:14 -08:00
const hasValidity = ( type !== TEXTFIELD_TYPE_NUMBER && e . target . validity . valid ) || type === TEXTFIELD_TYPE_NUMBER ;
2021-01-03 04:03:18 -08:00
if ( ( required && ( val === '' || val === null ) ) || val === initialValue || ! hasValidity ) {
2020-12-29 02:51:56 -08:00
setHasChanged ( false ) ;
2020-12-28 01:11:26 -08:00
} else {
2021-01-03 04:03:18 -08:00
// show submit button
2020-12-29 02:51:56 -08:00
resetStates ( ) ;
setHasChanged ( true ) ;
setFieldValueForSubmit ( val ) ;
2020-12-28 01:11:26 -08:00
}
2021-01-03 04:10:08 -08:00
// if an extra onChange handler was sent in as a prop, let's run that too.
if ( onChange ) {
onChange ( ) ;
}
2020-12-29 02:51:56 -08:00
} ;
2021-01-03 00:26:26 -08:00
// if you blur a required field with an empty value, restore its original value
2020-12-29 02:51:56 -08:00
const handleBlur = e = > {
const val = e . target . value ;
2021-01-03 00:26:26 -08:00
if ( required && val === '' ) {
2020-12-29 02:51:56 -08:00
handleResetValue ( fieldName ) ;
}
2021-01-03 04:10:08 -08:00
// if an extra onBlur handler was sent in as a prop, let's run that too.
if ( onBlur ) {
onBlur ( ) ;
}
2020-12-29 02:51:56 -08:00
} ;
// how to get current value of input
2021-01-03 01:10:38 -08:00
const handleSubmit = async ( ) = > {
2021-01-03 00:26:26 -08:00
if ( ( required && fieldValueForSubmit !== '' ) || fieldValueForSubmit !== initialValue ) {
2021-01-03 01:10:38 -08:00
setSubmitStatus ( 'validating' ) ;
await postConfigUpdateToAPI ( {
apiPath ,
data : { value : fieldValueForSubmit } ,
onSuccess : ( ) = > {
2021-01-03 01:54:04 -08:00
setFieldInConfigState ( { fieldName , value : fieldValueForSubmit , path : configPath } ) ;
2021-01-03 01:10:38 -08:00
setSubmitStatus ( 'success' ) ;
} ,
onError : ( message : string ) = > {
setSubmitStatus ( 'error' ) ;
setSubmitStatusMessage ( ` There was an error: ${ message } ` ) ;
} ,
} ) ;
resetTimer = setTimeout ( resetStates , RESET_TIMEOUT ) ;
2021-01-03 00:26:26 -08:00
// if an extra onSubmit handler was sent in as a prop, let's run that too.
if ( onSubmit ) {
onSubmit ( ) ;
}
2020-12-29 02:51:56 -08:00
}
}
2021-01-03 00:26:26 -08:00
// display the appropriate Ant text field
let Field = Input as typeof Input | typeof InputNumber | typeof Input . TextArea | typeof Input . Password ;
2020-12-29 02:51:56 -08:00
let fieldProps = { } ;
if ( type === TEXTFIELD_TYPE_TEXTAREA ) {
Field = Input . TextArea ;
fieldProps = {
autoSize : true ,
} ;
} else if ( type === TEXTFIELD_TYPE_PASSWORD ) {
Field = Input . Password ;
fieldProps = {
visibilityToggle : true ,
} ;
} else if ( type === TEXTFIELD_TYPE_NUMBER ) {
Field = InputNumber ;
2021-01-03 04:03:18 -08:00
fieldProps = {
type : 'number' ,
2021-01-09 13:12:14 -08:00
min : 1 ,
2021-01-03 04:03:18 -08:00
max : ( 10 * * maxLength ) - 1 ,
onKeyDown : ( e : React.KeyboardEvent ) = > {
if ( e . target . value . length > maxLength - 1 )
e . preventDefault ( )
return false ;
}
} ;
} else if ( type === TEXTFIELD_TYPE_URL ) {
fieldProps = {
type : 'url' ,
} ;
2020-12-28 01:11:26 -08:00
}
return (
2021-01-09 13:12:14 -08:00
< div className = { ` textfield-container type- ${ type } ` } >
2020-12-29 02:51:56 -08:00
< div className = "textfield" >
2021-01-03 01:54:04 -08:00
< InfoTip tip = { tip } / >
2020-12-29 02:51:56 -08:00
< Form.Item
label = { label }
name = { fieldName }
2021-01-01 18:23:23 -08:00
hasFeedback
2020-12-29 02:51:56 -08:00
validateStatus = { submitStatus }
help = { submitStatusMessage }
2021-01-03 00:26:26 -08:00
required = { required }
2020-12-29 02:51:56 -08:00
>
< Field
2021-01-03 01:10:38 -08:00
className = { ` field field- ${ fieldName } ` }
2020-12-29 02:51:56 -08:00
allowClear
2021-01-03 00:26:26 -08:00
placeholder = { placeholder }
2020-12-29 02:51:56 -08:00
maxLength = { maxLength }
onChange = { handleChange }
onBlur = { handleBlur }
2021-01-03 00:26:26 -08:00
disabled = { disabled }
2020-12-29 02:51:56 -08:00
{ . . . fieldProps }
/ >
< / Form.Item >
2020-12-28 01:11:26 -08:00
< / div >
2020-12-29 02:51:56 -08:00
{ hasChanged ? < Button type = "primary" size = "small" className = "submit-button" onClick = { handleSubmit } > Update < / Button > : null }
2020-12-26 19:44:09 -08:00
< / div >
) ;
}