diff --git a/ui/src/components/ControlWrapper/ControlWrapper.tsx b/ui/src/components/ControlWrapper/ControlWrapper.tsx index 1bcef56ac..ee200f31a 100644 --- a/ui/src/components/ControlWrapper/ControlWrapper.tsx +++ b/ui/src/components/ControlWrapper/ControlWrapper.tsx @@ -6,6 +6,7 @@ import CONTROL_TYPE_MAP, { ComponentTypes } from '../../constants/ControlTypeMap import { AnyEntity, UtilControlWrapper } from '../BaseFormView/BaseFormTypes'; import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes'; import CustomControl from '../CustomControl/CustomControl'; +import { Mode } from '../../constants/modes'; const CustomElement = styled.div``; @@ -26,7 +27,7 @@ const ControlGroupWrapper = styled(ControlGroup).attrs((props: { dataName: strin `; interface ControlWrapperProps { - mode: string; + mode: Mode; utilityFuncts: UtilControlWrapper; value: AcceptableFormValueOrNullish; display: boolean; @@ -130,6 +131,7 @@ class ControlWrapper extends React.PureComponent { this.props?.modifiedEntitiesData?.required || this.props.entity?.required === undefined ? 'oauth_field' in (this.props.entity || {}) // if required is undefined use true for oauth fields and false for others : this.props.entity?.required; // if required present use required + const label = this.props?.modifiedEntitiesData?.label || this?.props?.entity?.label || ''; return ( this.props.display && ( @@ -142,6 +144,7 @@ class ControlWrapper extends React.PureComponent { dataName={this?.props?.entity.field} labelWidth={240} required={isFieldRequired} + label={label} > {rowView} diff --git a/ui/src/components/CustomControl.jsx b/ui/src/components/CustomControl.tsx similarity index 55% rename from ui/src/components/CustomControl.jsx rename to ui/src/components/CustomControl.tsx index e8dfe38b4..c3d42fa43 100644 --- a/ui/src/components/CustomControl.jsx +++ b/ui/src/components/CustomControl.tsx @@ -1,12 +1,54 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { _ } from '@splunk/ui-utils/i18n'; import { getUnifiedConfigs } from '../util/util'; import { getBuildDirPath } from '../util/script'; +import { AcceptableFormValueOrNullish } from '../types/components/shareableTypes'; +import { UtilBaseForm } from './BaseFormView/BaseFormTypes'; +import { GlobalConfig } from '../types/globalConfig/globalConfig'; +import { Mode } from '../constants/modes'; -class CustomControl extends Component { - static loadCustomControl = (module, type, appName) => +interface IData { + value: AcceptableFormValueOrNullish; + mode: Mode; + serviceName: string; +} + +interface ICustomCompClass { + new ( + config: GlobalConfig, + data: IData, + setValue: (field: string, newValue: AcceptableFormValueOrNullish) => void, + util: UtilBaseForm, + el?: HTMLElement + ): { + render: () => void; + validation?: (submittedField: string, submittedValue: string) => void; + }; +} + +interface ICustomCompProps { + data: IData; + field: string; + handleChange: (field: string, newValue: AcceptableFormValueOrNullish) => void; + controlOptions: { src: string; type: string }; + addCustomValidator: ( + field: string, + validatorFunc: (submittedField: string, submittedValue: string) => void + ) => void; + utilCustomFunctions: UtilBaseForm; +} + +interface State { + loading: boolean; +} + +class CustomControl extends Component { + static loadCustomControl = ( + module: string, + type: string, + appName: string + ): Promise => new Promise((resolve) => { if (type === 'external') { import(/* webpackIgnore: true */ `${getBuildDirPath()}/custom/${module}.js`).then( @@ -16,13 +58,18 @@ class CustomControl extends Component { } ); } else { + // @ts-expect-error typeof __non_webpack_require__ is not known during bundle __non_webpack_require__([`app/${appName}/js/build/custom/${module}`], (Control) => { resolve(Control); }); } }); - constructor(props) { + shouldRender: boolean; + + el?: HTMLElement; + + constructor(props: ICustomCompProps) { super(props); this.state = { loading: true, @@ -41,10 +88,10 @@ class CustomControl extends Component { ).then((Control) => { const customControl = new Control( globalConfig, - this.el, this.props.data, this.setValue, - this.props.utilCustomFunctions + this.props.utilCustomFunctions, + this.el ); customControl.render(); @@ -55,7 +102,7 @@ class CustomControl extends Component { }); } - shouldComponentUpdate(nextProps, nextState) { + shouldComponentUpdate(nextProps: ICustomCompProps, nextState: State) { if (!nextState.loading && this.shouldRender) { this.shouldRender = false; return true; @@ -63,7 +110,7 @@ class CustomControl extends Component { return false; } - setValue = (newValue) => { + setValue = (newValue: AcceptableFormValueOrNullish) => { this.props.handleChange(this.props.field, newValue); }; @@ -74,7 +121,9 @@ class CustomControl extends Component { { { - this.el = el; + if (el) { + this.el = el; + } }} style={{ visibility: this.state.loading ? 'hidden' : 'visible' }} /> @@ -84,13 +133,4 @@ class CustomControl extends Component { } } -CustomControl.propTypes = { - data: PropTypes.object, - field: PropTypes.string, - handleChange: PropTypes.func, - controlOptions: PropTypes.object, - addCustomValidator: PropTypes.func, - utilCustomFunctions: PropTypes.object, -}; - export default CustomControl; diff --git a/ui/src/components/CustomControl/CustomControl.tsx b/ui/src/components/CustomControl/CustomControl.tsx index 59697288a..bbb2967dd 100644 --- a/ui/src/components/CustomControl/CustomControl.tsx +++ b/ui/src/components/CustomControl/CustomControl.tsx @@ -5,20 +5,21 @@ import { getBuildDirPath } from '../../util/script'; import { AcceptableFormValueOrNullish } from '../../types/components/shareableTypes'; import { UtilBaseForm } from '../BaseFormView/BaseFormTypes'; import { GlobalConfig } from '../../types/globalConfig/globalConfig'; +import { Mode } from '../../constants/modes'; interface IData { value: AcceptableFormValueOrNullish; - mode: string; + mode: Mode; serviceName: string; } interface ICustomCompClass { new ( config: GlobalConfig, - el: HTMLElement | undefined, data: IData, setValue: (field: string, newValue: AcceptableFormValueOrNullish) => void, - util: UtilBaseForm + util: UtilBaseForm, + el?: HTMLElement ): { render: () => void; validation?: (submittedField: string, submittedValue: string) => void; @@ -83,13 +84,13 @@ class CustomControl extends React.Component this.props.controlOptions.src, this.props.controlOptions.type, appName - ).then((Control: ICustomCompClass) => { + ).then((Control) => { const customControl = new Control( globalConfig, - this.el, this.props.data, this.setValue, - this.props.utilCustomFunctions + this.props.utilCustomFunctions, + this.el ); customControl?.render(); diff --git a/ui/src/components/CustomControl/CustomControlMockForTest.ts b/ui/src/components/CustomControl/CustomControlMockForTest.ts index 01218a7e2..1823816a6 100644 --- a/ui/src/components/CustomControl/CustomControlMockForTest.ts +++ b/ui/src/components/CustomControl/CustomControlMockForTest.ts @@ -6,7 +6,7 @@ import { UtilControlWrapper } from '../BaseFormView/BaseFormTypes'; export class CustomControlMockForTest { globalConfig: GlobalConfig; - el: Element; + el?: Element; data: { mode: Mode; serviceName: string; value: AcceptableFormValueOrNullish }; @@ -25,10 +25,10 @@ export class CustomControlMockForTest { */ constructor( globalConfig: GlobalConfig, - el: Element, data: { mode: Mode; serviceName: string; value: AcceptableFormValueOrNullish }, setValue: (newValue: AcceptableFormValueOrNullish) => void, - util: UtilControlWrapper + util: UtilControlWrapper, + el?: Element ) { this.globalConfig = globalConfig; this.el = el; @@ -53,6 +53,10 @@ export class CustomControlMockForTest { } render() { + if (!this.el) { + return this; + } + const options = ['input_default', 'input_one', 'input_two', 'input_three'].map( (value) => `