diff options
Diffstat (limited to 'openecomp-ui/src/nfvo-components/input')
10 files changed, 1363 insertions, 996 deletions
diff --git a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx index 82fbe1deed..3973ae8c5d 100644 --- a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx @@ -19,99 +19,118 @@ import ReactDOM from 'react-dom'; import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import Input from 'nfvo-components/input/validation/InputWrapper.jsx'; -const ExpandableInputClosed = ({iconType, onClick}) => ( - <SVGIcon className='expandable-input-wrapper closed' data-test-id='expandable-input-closed' name={iconType} onClick={onClick} /> +const ExpandableInputClosed = ({ iconType, onClick }) => ( + <SVGIcon + className="expandable-input-wrapper closed" + data-test-id="expandable-input-closed" + name={iconType} + onClick={onClick} + /> ); class ExpandableInputOpened extends React.Component { - componentDidMount(){ - this.rawDomNode = ReactDOM.findDOMNode(this.searchInputNode.inputWrapper); - this.rawDomNode.focus(); - } + componentDidMount() { + this.rawDomNode = ReactDOM.findDOMNode( + this.searchInputNode.inputWrapper + ); + this.rawDomNode.focus(); + } - componentWillReceiveProps(newProps){ - if (!newProps.value){ - if (!(document.activeElement === this.rawDomNode)){ - this.props.handleBlur(); - } - } - } + componentWillReceiveProps(newProps) { + if (!newProps.value) { + if (!(document.activeElement === this.rawDomNode)) { + this.props.handleBlur(); + } + } + } - handleClose(){ - this.props.onChange(''); - this.rawDomNode.focus(); - } + handleClose() { + this.props.onChange(''); + this.rawDomNode.focus(); + } - handleKeyDown(e){ - if (e.key === 'Escape'){ - e.preventDefault(); - if (this.props.value) { - this.handleClose(); - } else { - this.rawDomNode.blur(); - } - }; - } + handleKeyDown(e) { + if (e.key === 'Escape') { + e.preventDefault(); + if (this.props.value) { + this.handleClose(); + } else { + this.rawDomNode.blur(); + } + } + } - render() { - let {iconType, value, onChange, handleBlur} = this.props; - return ( - <div className='expandable-input-wrapper opened' key='expandable'> - <Input - type='text' - data-test-id='expandable-input-opened' - value={value} - ref={(input) => this.searchInputNode = input} - className='expandable-active' - groupClassName='expandable-input-control' - onChange={e => onChange(e)} - onKeyDown={e => this.handleKeyDown(e)} - onBlur={handleBlur}/> - {value && <SVGIcon data-test-id='expandable-input-close-btn' onClick={() => this.handleClose()} name='close' />} - {!value && <SVGIcon name={iconType} onClick={handleBlur}/>} - </div> - ); - } + render() { + let { iconType, value, onChange, handleBlur } = this.props; + return ( + <div className="expandable-input-wrapper opened" key="expandable"> + <Input + type="text" + data-test-id="expandable-input-opened" + value={value} + ref={input => (this.searchInputNode = input)} + className="expandable-active" + groupClassName="expandable-input-control" + onChange={e => onChange(e)} + onKeyDown={e => this.handleKeyDown(e)} + onBlur={handleBlur} + /> + {value && ( + <SVGIcon + data-test-id="expandable-input-close-btn" + onClick={() => this.handleClose()} + name="close" + /> + )} + {!value && <SVGIcon name={iconType} onClick={handleBlur} />} + </div> + ); + } } class ExpandableInput extends React.Component { + static propTypes = { + iconType: PropTypes.string, + onChange: PropTypes.func, + value: PropTypes.string + }; - static propTypes = { - iconType: PropTypes.string, - onChange: PropTypes.func, - value: PropTypes.string - }; + state = { showInput: false }; - state = {showInput: false}; + closeInput() { + if (!this.props.value) { + this.setState({ showInput: false }); + } + } - closeInput(){ - if (!this.props.value) { - this.setState({showInput: false}); - } - } + getValue() { + return this.props.value; + } - getValue(){ - return this.props.value; - } - - render(){ - let {iconType, value, onChange = false} = this.props; - return ( - <div className='expandable-input-top'> - {this.state.showInput && - <ExpandableInputOpened - key='open' - iconType={iconType} - onChange={onChange} - value={value} - handleKeyDown={(e) => this.handleKeyDown(e)} - handleBlur={() => this.closeInput()}/> - } - {!this.state.showInput && <ExpandableInputClosed key='closed' iconType={iconType} onClick={() => this.setState({showInput: true})} />} - </div> - ); - } + render() { + let { iconType, value, onChange = false } = this.props; + return ( + <div className="expandable-input-top"> + {this.state.showInput && ( + <ExpandableInputOpened + key="open" + iconType={iconType} + onChange={onChange} + value={value} + handleKeyDown={e => this.handleKeyDown(e)} + handleBlur={() => this.closeInput()} + /> + )} + {!this.state.showInput && ( + <ExpandableInputClosed + key="closed" + iconType={iconType} + onClick={() => this.setState({ showInput: true })} + /> + )} + </div> + ); + } } - export default ExpandableInput; diff --git a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx index 03c727379e..b0e0d87d7c 100644 --- a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx @@ -26,43 +26,51 @@ * or * https://github.com/JedWatson/react-select */ -import React, {Component} from 'react'; +import React, { Component } from 'react'; import Select from 'react-select'; class SelectInput extends Component { + inputValue = []; - inputValue = []; + render() { + let { label, value, ...other } = this.props; + const dataTestId = this.props['data-test-id'] + ? { 'data-test-id': this.props['data-test-id'] } + : {}; + return ( + <div + {...dataTestId} + className="validation-input-wrapper dropdown-multi-select"> + <div className="form-group"> + {label && <label className="control-label">{label}</label>} + <Select + ref="_myInput" + onChange={value => this.onSelectChanged(value)} + {...other} + value={value} + /> + </div> + </div> + ); + } - render() { - let {label, value, ...other} = this.props; - const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; - return ( - <div {...dataTestId} className='validation-input-wrapper dropdown-multi-select'> - <div className='form-group'> - {label && <label className='control-label'>{label}</label>} - <Select ref='_myInput' onChange={value => this.onSelectChanged(value)} {...other} value={value} /> - </div> - </div> - ); - } + getValue() { + return this.inputValue && this.inputValue.length ? this.inputValue : ''; + } - getValue() { - return this.inputValue && this.inputValue.length ? this.inputValue : ''; - } + onSelectChanged(value) { + this.props.onMultiSelectChanged(value); + } - onSelectChanged(value) { - this.props.onMultiSelectChanged(value); - } - - componentDidMount() { - let {value} = this.props; - this.inputValue = value ? value : []; - } - componentDidUpdate() { - if (this.inputValue !== this.props.value) { - this.inputValue = this.props.value; - } - } + componentDidMount() { + let { value } = this.props; + this.inputValue = value ? value : []; + } + componentDidUpdate() { + if (this.inputValue !== this.props.value) { + this.inputValue = this.props.value; + } + } } export default SelectInput; diff --git a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx index 31a8a66d86..947570fa29 100644 --- a/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/ToggleInput.jsx @@ -16,55 +16,60 @@ import React from 'react'; import PropTypes from 'prop-types'; -export default -class ToggleInput extends React.Component { +export default class ToggleInput extends React.Component { + static propTypes = { + label: PropTypes.node, + value: PropTypes.bool, + onChange: PropTypes.func, + disabled: PropTypes.bool + }; - static propTypes = { - label: PropTypes.node, - value: PropTypes.bool, - onChange: PropTypes.func, - disabled: PropTypes.bool - } + static defaultProps = { + value: false, + label: '' + }; - static defaultProps = { - value: false, - label: '' - } + state = { + value: this.props.value + }; - state = { - value: this.props.value - } + status() { + return this.state.value ? 'on' : 'off'; + } - status() { - return this.state.value ? 'on' : 'off'; - } + render() { + let { label, disabled } = this.props; + let checked = this.status() === 'on'; + //TODO check onclick + return ( + <div + className="toggle-input-wrapper form-group" + onClick={!disabled && this.click}> + <div className="toggle-input-label">{label}</div> + <div className="toggle-switch"> + <input + className="toggle toggle-round-flat" + type="checkbox" + checked={checked} + readOnly + /> + <label /> + </div> + </div> + ); + } - render() { - let {label, disabled} = this.props; - let checked = this.status() === 'on'; - //TODO check onclick - return ( - <div className='toggle-input-wrapper form-group' onClick={!disabled && this.click}> - <div className='toggle-input-label'>{label}</div> - <div className='toggle-switch'> - <input className='toggle toggle-round-flat' type='checkbox' checked={checked} readOnly/> - <label></label> - </div> - </div> - ); - } + click = () => { + let value = !this.state.value; + this.setState({ value }); - click = () => { - let value = !this.state.value; - this.setState({value}); + let onChange = this.props.onChange; + if (onChange) { + onChange(value); + } + }; - let onChange = this.props.onChange; - if (onChange) { - onChange(value); - } - } - - getValue() { - return this.state.value; - } + getValue() { + return this.state.value; + } } diff --git a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx index a689c50778..7ab4c8242c 100644 --- a/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx +++ b/openecomp-ui/src/nfvo-components/input/dualListbox/DualListboxView.jsx @@ -19,136 +19,224 @@ import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; import Input from 'nfvo-components/input/validation/InputWrapper.jsx'; class DualListboxView extends React.Component { - - static propTypes = { - - availableList: PropTypes.arrayOf(PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired - })), - filterTitle: PropTypes.shape({ - left: PropTypes.string, - right: PropTypes.string - }), - selectedValuesList: PropTypes.arrayOf(PropTypes.string), - - onChange: PropTypes.func.isRequired - }; - - static defaultProps = { - selectedValuesList: [], - availableList: [], - filterTitle: { - left: '', - right: '' - } - }; - - state = { - availableListFilter: '', - selectedValuesListFilter: '', - selectedValues: [] - }; - - render() { - let {availableList, selectedValuesList, filterTitle, isReadOnlyMode} = this.props; - let {availableListFilter, selectedValuesListFilter} = this.state; - - let unselectedList = availableList.filter(availableItem => !selectedValuesList.find(value => value === availableItem.id)); - let selectedList = availableList.filter(availableItem => selectedValuesList.find(value => value === availableItem.id)); - selectedList = selectedList.sort((a, b) => selectedValuesList.indexOf(a.id) - selectedValuesList.indexOf(b.id)); - return ( - <div className='dual-list-box'> - {this.renderListbox(filterTitle.left, unselectedList, { - value: availableListFilter, - ref: 'availableListFilter', - disabled: isReadOnlyMode, - onChange: (value) => this.setState({availableListFilter: value}) - }, {ref: 'availableValues', disabled: isReadOnlyMode, testId: 'available',})} - {this.renderOperationsBar(isReadOnlyMode)} - {this.renderListbox(filterTitle.right, selectedList, { - value: selectedValuesListFilter, - ref: 'selectedValuesListFilter', - disabled: isReadOnlyMode, - onChange: (value) => this.setState({selectedValuesListFilter: value}) - }, {ref: 'selectedValues', disabled: isReadOnlyMode, testId: 'selected'})} - </div> - ); - } - - renderListbox(filterTitle, list, filterProps, props) { - let regExFilter = new RegExp(escape(filterProps.value), 'i'); - let matchedItems = list.filter(item => item.name.match(regExFilter)); - let unMatchedItems = list.filter(item => !item.name.match(regExFilter)); - return ( - <div className='dual-search-multi-select-section'> - <p>{filterTitle}</p> - <div className='dual-text-box-search search-wrapper'> - <Input data-test-id={`${props.testId}-search-input`} - name='search-input-control' type='text' - groupClassName='search-input-control' - {...filterProps}/> - <SVGIcon name='search' className='search-icon'/> - </div> - <Input - multiple - onChange={(event) => this.onSelectItems(event.target.selectedOptions)} - groupClassName='dual-list-box-multi-select' - type='select' - name='dual-list-box-multi-select' - data-test-id={`${props.testId}-select-input`} - disabled={props.disabled} - ref={props.ref}> - {matchedItems.map(item => this.renderOption(item.id, item.name))} - {matchedItems.length && unMatchedItems.length && <option style={{pointerEvents: 'none'}}>--------------------</option>} - {unMatchedItems.map(item => this.renderOption(item.id, item.name))} - </Input> - </div> - ); - } - - onSelectItems(selectedOptions) { - let selectedValues = Object.keys(selectedOptions).map((k) => selectedOptions[k].value); - this.setState({selectedValues}); - } - - renderOption(value, name) { - return (<option className='dual-list-box-multi-select-text' key={value} value={value}>{name}</option>); - } - - renderOperationsBar(isReadOnlyMode) { - return ( - <div className={`dual-list-options-bar${isReadOnlyMode ? ' disabled' : ''}`}> - {this.renderOperationBarButton(() => this.addToSelectedList(), 'angleRight')} - {this.renderOperationBarButton(() => this.removeFromSelectedList(), 'angleLeft')} - {this.renderOperationBarButton(() => this.addAllToSelectedList(), 'angleDoubleRight')} - {this.renderOperationBarButton(() => this.removeAllFromSelectedList(), 'angleDoubleLeft')} - </div> - ); - } - - renderOperationBarButton(onClick, iconName){ - return (<div className='dual-list-option' data-test-id={`operation-icon-${iconName}`} onClick={onClick}><SVGIcon name={iconName}/></div>); - } - - addToSelectedList() { - this.props.onChange(this.props.selectedValuesList.concat(this.state.selectedValues)); - this.setState({selectedValues: []}); - } - - removeFromSelectedList() { - const selectedValues = this.state.selectedValues; - this.props.onChange(this.props.selectedValuesList.filter(value => !selectedValues.find(selectedValue => selectedValue === value))); - this.setState({selectedValues: []}); - } - - addAllToSelectedList() { - this.props.onChange(this.props.availableList.map(item => item.id)); - } - - removeAllFromSelectedList() { - this.props.onChange([]); - } + static propTypes = { + availableList: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired + }) + ), + filterTitle: PropTypes.shape({ + left: PropTypes.string, + right: PropTypes.string + }), + selectedValuesList: PropTypes.arrayOf(PropTypes.string), + + onChange: PropTypes.func.isRequired + }; + + static defaultProps = { + selectedValuesList: [], + availableList: [], + filterTitle: { + left: '', + right: '' + } + }; + + state = { + availableListFilter: '', + selectedValuesListFilter: '', + selectedValues: [] + }; + + render() { + let { + availableList, + selectedValuesList, + filterTitle, + isReadOnlyMode + } = this.props; + let { availableListFilter, selectedValuesListFilter } = this.state; + + let unselectedList = availableList.filter( + availableItem => + !selectedValuesList.find(value => value === availableItem.id) + ); + let selectedList = availableList.filter(availableItem => + selectedValuesList.find(value => value === availableItem.id) + ); + selectedList = selectedList.sort( + (a, b) => + selectedValuesList.indexOf(a.id) - + selectedValuesList.indexOf(b.id) + ); + return ( + <div className="dual-list-box"> + {this.renderListbox( + filterTitle.left, + unselectedList, + { + value: availableListFilter, + ref: 'availableListFilter', + disabled: isReadOnlyMode, + onChange: value => + this.setState({ availableListFilter: value }) + }, + { + ref: 'availableValues', + disabled: isReadOnlyMode, + testId: 'available' + } + )} + {this.renderOperationsBar(isReadOnlyMode)} + {this.renderListbox( + filterTitle.right, + selectedList, + { + value: selectedValuesListFilter, + ref: 'selectedValuesListFilter', + disabled: isReadOnlyMode, + onChange: value => + this.setState({ selectedValuesListFilter: value }) + }, + { + ref: 'selectedValues', + disabled: isReadOnlyMode, + testId: 'selected' + } + )} + </div> + ); + } + + renderListbox(filterTitle, list, filterProps, props) { + let regExFilter = new RegExp(escape(filterProps.value), 'i'); + let matchedItems = list.filter(item => item.name.match(regExFilter)); + let unMatchedItems = list.filter(item => !item.name.match(regExFilter)); + return ( + <div className="dual-search-multi-select-section"> + <p>{filterTitle}</p> + <div className="dual-text-box-search search-wrapper"> + <Input + data-test-id={`${props.testId}-search-input`} + name="search-input-control" + type="text" + groupClassName="search-input-control" + {...filterProps} + /> + <SVGIcon name="search" className="search-icon" /> + </div> + <Input + multiple + onChange={event => + this.onSelectItems(event.target.selectedOptions) + } + groupClassName="dual-list-box-multi-select" + type="select" + name="dual-list-box-multi-select" + data-test-id={`${props.testId}-select-input`} + disabled={props.disabled} + ref={props.ref}> + {matchedItems.map(item => + this.renderOption(item.id, item.name) + )} + {matchedItems.length && + unMatchedItems.length && ( + <option style={{ pointerEvents: 'none' }}> + -------------------- + </option> + )} + {unMatchedItems.map(item => + this.renderOption(item.id, item.name) + )} + </Input> + </div> + ); + } + + onSelectItems(selectedOptions) { + let selectedValues = Object.keys(selectedOptions).map( + k => selectedOptions[k].value + ); + this.setState({ selectedValues }); + } + + renderOption(value, name) { + return ( + <option + className="dual-list-box-multi-select-text" + key={value} + value={value}> + {name} + </option> + ); + } + + renderOperationsBar(isReadOnlyMode) { + return ( + <div + className={`dual-list-options-bar${ + isReadOnlyMode ? ' disabled' : '' + }`}> + {this.renderOperationBarButton( + () => this.addToSelectedList(), + 'angleRight' + )} + {this.renderOperationBarButton( + () => this.removeFromSelectedList(), + 'angleLeft' + )} + {this.renderOperationBarButton( + () => this.addAllToSelectedList(), + 'angleDoubleRight' + )} + {this.renderOperationBarButton( + () => this.removeAllFromSelectedList(), + 'angleDoubleLeft' + )} + </div> + ); + } + + renderOperationBarButton(onClick, iconName) { + return ( + <div + className="dual-list-option" + data-test-id={`operation-icon-${iconName}`} + onClick={onClick}> + <SVGIcon name={iconName} /> + </div> + ); + } + + addToSelectedList() { + this.props.onChange( + this.props.selectedValuesList.concat(this.state.selectedValues) + ); + this.setState({ selectedValues: [] }); + } + + removeFromSelectedList() { + const selectedValues = this.state.selectedValues; + this.props.onChange( + this.props.selectedValuesList.filter( + value => + !selectedValues.find( + selectedValue => selectedValue === value + ) + ) + ); + this.setState({ selectedValues: [] }); + } + + addAllToSelectedList() { + this.props.onChange(this.props.availableList.map(item => item.id)); + } + + removeAllFromSelectedList() { + this.props.onChange([]); + } } export default DualListboxView; diff --git a/openecomp-ui/src/nfvo-components/input/validation/Form.jsx b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx index 6df0bf9009..62fc29a55c 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/Form.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx @@ -19,130 +19,160 @@ import PropTypes from 'prop-types'; import ValidationButtons from './ValidationButtons.jsx'; class Form extends React.Component { - - static defaultProps = { - hasButtons : true, - onSubmit : null, - onReset : null, - labledButtons: true, - onValidChange : null, - isValid: true, - submitButtonText: null, - cancelButtonText: null - }; - - static propTypes = { - isValid : PropTypes.bool, - formReady : PropTypes.bool, - isReadOnlyMode : PropTypes.bool, - hasButtons : PropTypes.bool, - onSubmit : PropTypes.func, - onReset : PropTypes.func, - labledButtons: PropTypes.bool, - submitButtonText: PropTypes.string, - cancelButtonText: PropTypes.string, - onValidChange : PropTypes.func, - onValidityChanged: PropTypes.func, - onValidateForm: PropTypes.func - }; - - constructor(props) { - super(props); - } - - - render() { - // eslint-disable-next-line no-unused-vars - let {isValid, onValidChange, onValidityChanged, onDataChanged, formReady, onValidateForm, isReadOnlyMode, hasButtons, onSubmit, labledButtons, submitButtonText, - cancelButtonText, children, ...formProps} = this.props; - return ( - <form {...formProps} ref={(form) => this.form = form} onSubmit={event => this.handleFormValidation(event)}> - <div className='validation-form-content'> - <fieldset disabled={isReadOnlyMode}> - {children} - </fieldset> - </div> - {hasButtons && - <ValidationButtons - labledButtons={labledButtons} - submitButtonText={submitButtonText} - cancelButtonText={cancelButtonText} - ref={(buttons) => this.buttons = buttons} - isReadOnlyMode={isReadOnlyMode}/>} - </form> - ); - } - - handleFormValidation(event) { - event.preventDefault(); - if (this.props.onValidateForm && !this.props.formReady){ - return this.props.onValidateForm(); - } else { - return this.handleFormSubmit(event); - } - } - handleFormSubmit(event) { - if (event) { - event.preventDefault(); - } - if(this.props.onSubmit) { - return this.props.onSubmit(event); - } - } - - componentDidMount() { - if (this.props.hasButtons) { - this.buttons.setState({isValid: this.props.isValid}); - } - } - - - - componentDidUpdate(prevProps) { - // only handling this programatically if the validation of the form is done outside of the view - // (example with a form that is dependent on the state of other forms) - if (prevProps.isValid !== this.props.isValid) { - if (this.props.hasButtons) { - this.buttons.setState({isValid: this.props.isValid}); - } - // callback in case form is part of bigger picture in view - if (this.props.onValidChange) { - this.props.onValidChange(this.props.isValid); - } - - // TODO - maybe this has to be part of componentWillUpdate - if(this.props.onValidityChanged) { - this.props.onValidityChanged(this.props.isValid); - } - } - if (this.props.formReady) { // if form validation succeeded -> continue with submit - this.handleFormSubmit(); - } - } - + static defaultProps = { + hasButtons: true, + onSubmit: null, + onReset: null, + labledButtons: true, + onValidChange: null, + isValid: true, + submitButtonText: null, + cancelButtonText: null + }; + + static propTypes = { + isValid: PropTypes.bool, + formReady: PropTypes.bool, + isReadOnlyMode: PropTypes.bool, + hasButtons: PropTypes.bool, + onSubmit: PropTypes.func, + onReset: PropTypes.func, + labledButtons: PropTypes.bool, + submitButtonText: PropTypes.string, + cancelButtonText: PropTypes.string, + onValidChange: PropTypes.func, + onValidityChanged: PropTypes.func, + onValidateForm: PropTypes.func + }; + + constructor(props) { + super(props); + } + render() { + /* eslint-disable no-unused-vars */ + let { + isValid, + onValidChange, + onValidityChanged, + onDataChanged, + formReady, + onValidateForm, + isReadOnlyMode, + hasButtons, + onSubmit, + labledButtons, + submitButtonText, + cancelButtonText, + children, + ...formProps + } = this.props; + /* eslint-enable no-unused-vars */ + return ( + <form + {...formProps} + ref={form => (this.form = form)} + onSubmit={event => this.handleFormValidation(event)}> + <div className="validation-form-content"> + <fieldset disabled={isReadOnlyMode}>{children}</fieldset> + </div> + {hasButtons && ( + <ValidationButtons + labledButtons={labledButtons} + submitButtonText={submitButtonText} + cancelButtonText={cancelButtonText} + ref={buttons => (this.buttons = buttons)} + isReadOnlyMode={isReadOnlyMode} + /> + )} + </form> + ); + } + + handleFormValidation(event) { + event.preventDefault(); + if (this.props.onValidateForm && !this.props.formReady) { + return this.props.onValidateForm(); + } else { + return this.handleFormSubmit(event); + } + } + handleFormSubmit(event) { + if (event) { + event.preventDefault(); + } + if (this.props.onSubmit) { + return this.props.onSubmit(event); + } + } + + componentDidMount() { + if (this.props.hasButtons) { + this.buttons.setState({ isValid: this.props.isValid }); + } + } + + componentDidUpdate(prevProps) { + // only handling this programatically if the validation of the form is done outside of the view + // (example with a form that is dependent on the state of other forms) + if (prevProps.isValid !== this.props.isValid) { + if (this.props.hasButtons) { + this.buttons.setState({ isValid: this.props.isValid }); + } + // callback in case form is part of bigger picture in view + if (this.props.onValidChange) { + this.props.onValidChange(this.props.isValid); + } + + // TODO - maybe this has to be part of componentWillUpdate + if (this.props.onValidityChanged) { + this.props.onValidityChanged(this.props.isValid); + } + } + if (this.props.formReady) { + // if form validation succeeded -> continue with submit + this.handleFormSubmit(); + } + } } export class TabsForm extends Form { - render() { - // eslint-disable-next-line no-unused-vars - let {submitButtonText, cancelButtonText, isValid, formReady, onValidateForm, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, onDataChanged, children, - ...formProps} = this.props; - return ( - <form {...formProps} ref={(form) => this.form = form} onSubmit={event => this.handleFormValidation(event)}> - <div className='validation-form-content'> - {children} - </div> - {hasButtons && - <ValidationButtons - labledButtons={labledButtons} - submitButtonText={submitButtonText} - cancelButtonText={cancelButtonText} - ref={buttons => this.buttons = buttons} - isReadOnlyMode={isReadOnlyMode}/> - } - </form> - ); - } + render() { + /* eslint-disable no-unused-vars */ + let { + submitButtonText, + cancelButtonText, + isValid, + formReady, + onValidateForm, + isReadOnlyMode, + hasButtons, + onSubmit, + labledButtons, + onValidChange, + onValidityChanged, + onDataChanged, + children, + ...formProps + } = this.props; + /* eslint-enable no-unused-vars */ + return ( + <form + {...formProps} + ref={form => (this.form = form)} + onSubmit={event => this.handleFormValidation(event)}> + <div className="validation-form-content">{children}</div> + {hasButtons && ( + <ValidationButtons + labledButtons={labledButtons} + submitButtonText={submitButtonText} + cancelButtonText={cancelButtonText} + ref={buttons => (this.buttons = buttons)} + isReadOnlyMode={isReadOnlyMode} + /> + )} + </form> + ); + } } export default Form; diff --git a/openecomp-ui/src/nfvo-components/input/validation/Input.jsx b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx index 33cea933b5..a5d6f4fd7a 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/Input.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx @@ -25,191 +25,249 @@ import Tooltip from 'react-bootstrap/lib/Tooltip.js'; import Datepicker from 'nfvo-components/datepicker/Datepicker.jsx'; class Input extends React.Component { + state = { + value: this.props.value, + checked: this.props.checked, + selectedValues: [] + }; - state = { - value: this.props.value, - checked: this.props.checked, - selectedValues: [] - }; + render() { + /* eslint-disable no-unused-vars */ + const { + label, + isReadOnlyMode, + value, + onBlur, + onKeyDown, + type, + disabled, + checked, + name + } = this.props; + const { + groupClassName, + isValid = true, + errorText, + isRequired, + overlayPos, + ...inputProps + } = this.props; + const { + dateFormat, + startDate, + endDate, + selectsStart, + selectsEnd + } = this.props; // Date Props + /* eslint-enable no-unused-vars */ + let wrapperClassName = + type !== 'radio' + ? 'validation-input-wrapper' + : 'validation-radio-wrapper'; + if (disabled) { + wrapperClassName += ' disabled'; + } + return ( + <div className={wrapperClassName}> + <FormGroup + className={classNames('form-group', [groupClassName], { + required: isRequired, + 'has-error': !isValid + })}> + {label && + (type !== 'checkbox' && type !== 'radio') && ( + <label className="control-label">{label}</label> + )} + {type === 'text' && ( + <FormControl + bsClass={'form-control input-options-other'} + onChange={e => this.onChange(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + onBlur={onBlur} + onKeyDown={onKeyDown} + value={value || ''} + inputRef={input => (this.input = input)} + type={type} + data-test-id={this.props['data-test-id']} + /> + )} + {type === 'number' && ( + <FormControl + bsClass={'form-control input-options-other'} + onChange={e => this.onChange(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + onBlur={onBlur} + onKeyDown={onKeyDown} + value={value !== undefined ? value : ''} + inputRef={input => (this.input = input)} + type={type} + data-test-id={this.props['data-test-id']} + /> + )} - render() { - const {label, isReadOnlyMode, value, onBlur, onKeyDown, type, disabled, checked, name} = this.props; - // eslint-disable-next-line no-unused-vars - const {groupClassName, isValid = true, errorText, isRequired, overlayPos, ...inputProps} = this.props; - const {dateFormat, startDate, endDate, selectsStart, selectsEnd} = this.props; // Date Props - let wrapperClassName = (type !== 'radio') ? 'validation-input-wrapper' : 'validation-radio-wrapper'; - if (disabled) { - wrapperClassName += ' disabled'; - } - return( - <div className={wrapperClassName}> - <FormGroup className={classNames('form-group', [groupClassName], {'required' : isRequired , 'has-error' : !isValid})} > - {(label && (type !== 'checkbox' && type !== 'radio')) && <label className='control-label'>{label}</label>} - {type === 'text' && - <FormControl - bsClass={'form-control input-options-other'} - onChange={(e) => this.onChange(e)} - disabled={isReadOnlyMode || Boolean(disabled)} - onBlur={onBlur} - onKeyDown={onKeyDown} - value={value || ''} - inputRef={(input) => this.input = input} - type={type} - data-test-id={this.props['data-test-id']}/>} - {type === 'number' && - <FormControl - bsClass={'form-control input-options-other'} - onChange={(e) => this.onChange(e)} - disabled={isReadOnlyMode || Boolean(disabled)} - onBlur={onBlur} - onKeyDown={onKeyDown} - value={(value !== undefined) ? value : ''} - inputRef={(input) => this.input = input} - type={type} - data-test-id={this.props['data-test-id']}/>} + {type === 'textarea' && ( + <FormControl + className="form-control input-options-other" + disabled={isReadOnlyMode || Boolean(disabled)} + value={value || ''} + onBlur={onBlur} + onKeyDown={onKeyDown} + componentClass={type} + onChange={e => this.onChange(e)} + inputRef={input => (this.input = input)} + data-test-id={this.props['data-test-id']} + /> + )} - {type === 'textarea' && - <FormControl - className='form-control input-options-other' - disabled={isReadOnlyMode || Boolean(disabled)} - value={value || ''} - onBlur={onBlur} - onKeyDown={onKeyDown} - componentClass={type} - onChange={(e) => this.onChange(e)} - inputRef={(input) => this.input = input} - data-test-id={this.props['data-test-id']}/>} + {type === 'checkbox' && ( + <Checkbox + className={classNames({ + required: isRequired, + 'has-error': !isValid + })} + onChange={e => this.onChangeCheckBox(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + checked={checked} + data-test-id={this.props['data-test-id']}> + {label} + </Checkbox> + )} - {type === 'checkbox' && - <Checkbox - className={classNames({'required' : isRequired , 'has-error' : !isValid})} - onChange={(e)=>this.onChangeCheckBox(e)} - disabled={isReadOnlyMode || Boolean(disabled)} - checked={checked} - data-test-id={this.props['data-test-id']}>{label}</Checkbox>} + {type === 'radio' && ( + <Radio + name={name} + checked={checked} + disabled={isReadOnlyMode || Boolean(disabled)} + value={value} + onChange={isChecked => + this.onChangeRadio(isChecked) + } + inputRef={input => (this.input = input)} + label={label} + data-test-id={this.props['data-test-id']} + /> + )} + {type === 'select' && ( + <FormControl + onClick={e => this.optionSelect(e)} + className="custom-select" + componentClass={type} + inputRef={input => (this.input = input)} + name={name} + {...inputProps} + data-test-id={this.props['data-test-id']} + /> + )} + {type === 'date' && ( + <Datepicker + date={value} + format={dateFormat} + startDate={startDate} + endDate={endDate} + inputRef={input => (this.input = input)} + onChange={this.props.onChange} + disabled={isReadOnlyMode || Boolean(disabled)} + data-test-id={this.props['data-test-id']} + selectsStart={selectsStart} + selectsEnd={selectsEnd} + /> + )} + </FormGroup> + {this.renderErrorOverlay()} + </div> + ); + } - {type === 'radio' && - <Radio name={name} - checked={checked} - disabled={isReadOnlyMode || Boolean(disabled)} - value={value} - onChange={(isChecked)=>this.onChangeRadio(isChecked)} - inputRef={(input) => this.input = input} - label={label} - data-test-id={this.props['data-test-id']} />} - {type === 'select' && - <FormControl onClick={ (e) => this.optionSelect(e) } - className='custom-select' - componentClass={type} - inputRef={(input) => this.input = input} - name={name} {...inputProps} - data-test-id={this.props['data-test-id']}/>} - {type === 'date' && - <Datepicker - date={value} - format={dateFormat} - startDate={startDate} - endDate={endDate} - inputRef={(input) => this.input = input} - onChange={this.props.onChange} - disabled={isReadOnlyMode || Boolean(disabled)} - data-test-id={this.props['data-test-id']} - selectsStart={selectsStart} - selectsEnd={selectsEnd} />} - </FormGroup> - { this.renderErrorOverlay() } - </div> - ); - } + getValue() { + return this.props.type !== 'select' + ? this.state.value + : this.state.selectedValues; + } - getValue() { - return this.props.type !== 'select' ? this.state.value : this.state.selectedValues; - } + getChecked() { + return this.state.checked; + } - getChecked() { - return this.state.checked; - } + optionSelect(e) { + let selectedValues = []; + if (e.target.value) { + selectedValues.push(e.target.value); + } + this.setState({ + selectedValues + }); + } - optionSelect(e) { - let selectedValues = []; - if (e.target.value) { - selectedValues.push(e.target.value); - } - this.setState({ - selectedValues - }); - } + onChange(e) { + const { onChange, type } = this.props; + let value = e.target.value; + if (type === 'number') { + if (value === '') { + value = undefined; + } else { + value = Number(value); + } + } + this.setState({ + value + }); + onChange(value); + } - onChange(e) { - const {onChange, type} = this.props; - let value = e.target.value; - if (type === 'number') { - if (value === '') { - value = undefined; - } else { - value = Number(value); - } - } - this.setState({ - value - }); - onChange(value); - } + onChangeCheckBox(e) { + let { onChange } = this.props; + let checked = e.target.checked; + this.setState({ + checked + }); + onChange(checked); + } - onChangeCheckBox(e) { - let {onChange} = this.props; - let checked = e.target.checked; - this.setState({ - checked - }); - onChange(checked); - } + onChangeRadio(isChecked) { + let { onChange } = this.props; + this.setState({ + checked: isChecked + }); + onChange(this.state.value); + } - onChangeRadio(isChecked) { - let {onChange} = this.props; - this.setState({ - checked: isChecked - }); - onChange(this.state.value); - } + focus() { + ReactDOM.findDOMNode(this.input).focus(); + } - focus() { - ReactDOM.findDOMNode(this.input).focus(); - } + renderErrorOverlay() { + let position = 'right'; + const { errorText = '', isValid = true, type, overlayPos } = this.props; - renderErrorOverlay() { - let position = 'right'; - const {errorText = '', isValid = true, type, overlayPos} = this.props; - - if (overlayPos) { - position = overlayPos; - } - else if (type === 'text' - || type === 'email' - || type === 'number' - || type === 'radio' - || type === 'password' - || type === 'date') { - position = 'bottom'; - } - - return ( - <Overlay - show={!isValid} - placement={position} - target={() => { - let target = ReactDOM.findDOMNode(this.input); - return target.offsetParent ? target : undefined; - }} - container={this}> - <Tooltip - id={`error-${errorText.replace(' ', '-')}`} - className='validation-error-message'> - {errorText} - </Tooltip> - </Overlay> - ); - } + if (overlayPos) { + position = overlayPos; + } else if ( + type === 'text' || + type === 'email' || + type === 'number' || + type === 'radio' || + type === 'password' || + type === 'date' + ) { + position = 'bottom'; + } + return ( + <Overlay + show={!isValid} + placement={position} + target={() => { + let target = ReactDOM.findDOMNode(this.input); + return target.offsetParent ? target : undefined; + }} + container={this}> + <Tooltip + id={`error-${errorText.replace(' ', '-')}`} + className="validation-error-message"> + {errorText} + </Tooltip> + </Overlay> + ); + } } -export default Input; +export default Input; diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx index 11b07ba9da..019b6a5c70 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx @@ -22,260 +22,341 @@ import Select from 'nfvo-components/input/SelectInput.jsx'; import Overlay from 'react-bootstrap/lib/Overlay.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; -export const other = {OTHER: 'Other'}; +export const other = { OTHER: 'Other' }; class InputOptions extends React.Component { + static propTypes = { + values: PropTypes.arrayOf( + PropTypes.shape({ + enum: PropTypes.string, + title: PropTypes.string + }) + ), + isEnabledOther: PropTypes.bool, + label: PropTypes.string, + selectedValue: PropTypes.string, + multiSelectedEnum: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.array + ]), + selectedEnum: PropTypes.string, + otherValue: PropTypes.string, + overlayPos: PropTypes.string, + onEnumChange: PropTypes.func, + onOtherChange: PropTypes.func, + onBlur: PropTypes.func, + isRequired: PropTypes.bool, + isMultiSelect: PropTypes.bool, + isValid: PropTypes.bool, + disabled: PropTypes.bool + }; - static propTypes = { - values: PropTypes.arrayOf(PropTypes.shape({ - enum: PropTypes.string, - title: PropTypes.string - })), - isEnabledOther: PropTypes.bool, - label: PropTypes.string, - selectedValue: PropTypes.string, - multiSelectedEnum: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.array - ]), - selectedEnum: PropTypes.string, - otherValue: PropTypes.string, - overlayPos: PropTypes.string, - onEnumChange: PropTypes.func, - onOtherChange: PropTypes.func, - onBlur: PropTypes.func, - isRequired: PropTypes.bool, - isMultiSelect: PropTypes.bool, - isValid: PropTypes.bool, - disabled: PropTypes.bool - }; + state = { + otherInputDisabled: !this.props.otherValue + }; - state = { - otherInputDisabled: !this.props.otherValue - }; + oldProps = { + selectedEnum: '', + otherValue: '', + multiSelectedEnum: [] + }; - oldProps = { - selectedEnum: '', - otherValue: '', - multiSelectedEnum: [] - }; + render() { + let { + label, + isRequired, + values, + otherValue, + onOtherChange, + isMultiSelect, + onBlur, + multiSelectedEnum, + selectedEnum, + isValid, + children, + isReadOnlyMode + } = this.props; + const dataTestId = this.props['data-test-id'] + ? { 'data-test-id': this.props['data-test-id'] } + : {}; + let currentMultiSelectedEnum = []; + let currentSelectedEnum = ''; + let otherInputDisabled = + (isMultiSelect && + (multiSelectedEnum === undefined || + multiSelectedEnum.length === 0 || + multiSelectedEnum[0] !== other.OTHER)) || + (!isMultiSelect && + (selectedEnum === undefined || selectedEnum !== other.OTHER)); + if (isMultiSelect) { + currentMultiSelectedEnum = multiSelectedEnum; + if (!otherInputDisabled) { + currentSelectedEnum = multiSelectedEnum + ? multiSelectedEnum.toString() + : undefined; + } + } else if (selectedEnum) { + currentSelectedEnum = selectedEnum; + } + if (!onBlur) { + onBlur = () => {}; + } - render() { - let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, isValid, children, isReadOnlyMode} = this.props; - const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; - let currentMultiSelectedEnum = []; - let currentSelectedEnum = ''; - let otherInputDisabled = (isMultiSelect && (multiSelectedEnum === undefined || multiSelectedEnum.length === 0 || multiSelectedEnum[0] !== other.OTHER)) - || (!isMultiSelect && (selectedEnum === undefined || selectedEnum !== other.OTHER)); - if (isMultiSelect) { - currentMultiSelectedEnum = multiSelectedEnum; - if(!otherInputDisabled) { - currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined; - } - } - else if(selectedEnum){ - currentSelectedEnum = selectedEnum; - } - if (!onBlur) { - onBlur = () => {}; - } + return ( + <div className="validation-input-wrapper"> + <div + className={classNames('form-group', { + required: isRequired, + 'has-error': !isValid + })}> + {label && <label className="control-label">{label}</label>} + {isMultiSelect && otherInputDisabled ? ( + <Select + {...dataTestId} + ref={input => (this.input = input)} + value={currentMultiSelectedEnum} + className="options-input" + clearable={false} + required={isRequired} + disabled={ + isReadOnlyMode || Boolean(this.props.disabled) + } + onBlur={() => onBlur()} + onMultiSelectChanged={value => + this.multiSelectEnumChanged(value) + } + options={this.renderMultiSelectOptions(values)} + multi + /> + ) : ( + <div + className={classNames('input-options', { + 'has-error': !isValid + })}> + <select + {...dataTestId} + ref={input => (this.input = input)} + label={label} + className="form-control input-options-select" + value={currentSelectedEnum} + style={{ + width: otherInputDisabled ? '100%' : '100px' + }} + onBlur={() => onBlur()} + disabled={ + isReadOnlyMode || + Boolean(this.props.disabled) + } + onChange={value => this.enumChanged(value)} + type="select"> + {children || + (values && + values.length && + values.map((val, index) => + this.renderOptions(val, index) + ))} + {onOtherChange && ( + <option key="other" value={other.OTHER}> + {i18n(other.OTHER)} + </option> + )} + </select> - return( - <div className='validation-input-wrapper' > - <div className={classNames('form-group', {'required' : isRequired, 'has-error' : !isValid})} > - {label && <label className='control-label'>{label}</label>} - {isMultiSelect && otherInputDisabled ? - <Select - {...dataTestId} - ref={(input) => this.input = input} - value={currentMultiSelectedEnum} - className='options-input' - clearable={false} - required={isRequired} - disabled={isReadOnlyMode || Boolean(this.props.disabled)} - onBlur={() => onBlur()} - onMultiSelectChanged={value => this.multiSelectEnumChanged(value)} - options={this.renderMultiSelectOptions(values)} - multi/> : - <div className={classNames('input-options',{'has-error' : !isValid})} > - <select - {...dataTestId} - ref={(input) => this.input = input} - label={label} - className='form-control input-options-select' - value={currentSelectedEnum} - style={{'width' : otherInputDisabled ? '100%' : '100px'}} - onBlur={() => onBlur()} - disabled={isReadOnlyMode || Boolean(this.props.disabled)} - onChange={ value => this.enumChanged(value)} - type='select'> - {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))} - {onOtherChange && <option key='other' value={other.OTHER}>{i18n(other.OTHER)}</option>} - </select> + {!otherInputDisabled && ( + <div className="input-options-separator" /> + )} + <input + className="form-control input-options-other" + placeholder={i18n('other')} + ref={otherValue => + (this.otherValue = otherValue) + } + style={{ + display: otherInputDisabled + ? 'none' + : 'block' + }} + disabled={ + isReadOnlyMode || + Boolean(this.props.disabled) + } + value={otherValue || ''} + onBlur={() => onBlur()} + onChange={() => this.changedOtherInput()} + /> + </div> + )} + </div> + {this.renderErrorOverlay()} + </div> + ); + } - {!otherInputDisabled && <div className='input-options-separator'/>} - <input - className='form-control input-options-other' - placeholder={i18n('other')} - ref={(otherValue) => this.otherValue = otherValue} - style={{'display' : otherInputDisabled ? 'none' : 'block'}} - disabled={isReadOnlyMode || Boolean(this.props.disabled)} - value={otherValue || ''} - onBlur={() => onBlur()} - onChange={() => this.changedOtherInput()}/> - </div> - } - </div> - { this.renderErrorOverlay() } - </div> - ); - } + renderOptions(val, index) { + return ( + <option key={index} value={val.enum}> + {val.title} + </option> + ); + } - renderOptions(val, index){ - return ( - <option key={index} value={val.enum}>{val.title}</option> - ); - } + renderMultiSelectOptions(values) { + let { onOtherChange } = this.props; + let optionsList = []; + if (onOtherChange) { + optionsList = values + .map(option => { + return { + label: option.title, + value: option.enum + }; + }) + .concat([ + { + label: i18n(other.OTHER), + value: i18n(other.OTHER) + } + ]); + } else { + optionsList = values.map(option => { + return { + label: option.title, + value: option.enum + }; + }); + } + if (optionsList.length > 0 && optionsList[0].value === '') { + optionsList.shift(); + } + return optionsList; + } + renderErrorOverlay() { + let position = 'right'; + const { errorText = '', isValid = true, type, overlayPos } = this.props; - renderMultiSelectOptions(values) { - let {onOtherChange} = this.props; - let optionsList = []; - if (onOtherChange) { - optionsList = values.map(option => { - return { - label: option.title, - value: option.enum, - }; - }).concat([{ - label: i18n(other.OTHER), - value: i18n(other.OTHER), - }]); - } - else { - optionsList = values.map(option => { - return { - label: option.title, - value: option.enum, - }; - }); - } - if (optionsList.length > 0 && optionsList[0].value === '') { - optionsList.shift(); - } - return optionsList; - } + if (overlayPos) { + position = overlayPos; + } else if ( + type === 'text' || + type === 'email' || + type === 'number' || + type === 'password' + ) { + position = 'bottom'; + } - renderErrorOverlay() { - let position = 'right'; - const {errorText = '', isValid = true, type, overlayPos} = this.props; + return ( + <Overlay + show={!isValid} + placement={position} + target={() => { + let { otherInputDisabled } = this.state; + let target = otherInputDisabled + ? ReactDOM.findDOMNode(this.input) + : ReactDOM.findDOMNode(this.otherValue); + return target.offsetParent ? target : undefined; + }} + container={this}> + <Tooltip + id={`error-${errorText.replace(' ', '-')}`} + className="validation-error-message"> + {errorText} + </Tooltip> + </Overlay> + ); + } - if (overlayPos) { - position = overlayPos; - } - else if (type === 'text' - || type === 'email' - || type === 'number' - || type === 'password') { - position = 'bottom'; - } + getValue() { + let res = ''; + let { isMultiSelect } = this.props; + let { otherInputDisabled } = this.state; - return ( - <Overlay - show={!isValid} - placement={position} - target={() => { - let {otherInputDisabled} = this.state; - let target = otherInputDisabled ? ReactDOM.findDOMNode(this.input) : ReactDOM.findDOMNode(this.otherValue); - return target.offsetParent ? target : undefined; - }} - container={this}> - <Tooltip - id={`error-${errorText.replace(' ', '-')}`} - className='validation-error-message'> - {errorText} - </Tooltip> - </Overlay> - ); - } + if (otherInputDisabled) { + res = isMultiSelect ? this.input.getValue() : this.input.value; + } else { + res = this.otherValue.value; + } + return res; + } - getValue() { - let res = ''; - let {isMultiSelect} = this.props; - let {otherInputDisabled} = this.state; + enumChanged() { + let enumValue = this.input.value; + let { + onEnumChange, + onOtherChange, + isMultiSelect, + onChange + } = this.props; + this.setState({ + otherInputDisabled: + !Boolean(onOtherChange) || enumValue !== other.OTHER + }); - if (otherInputDisabled) { - res = isMultiSelect ? this.input.getValue() : this.input.value; - } else { - res = this.otherValue.value; - } - return res; - } + let value = isMultiSelect ? [enumValue] : enumValue; + if (onEnumChange) { + onEnumChange(value); + } + if (onChange) { + onChange(value); + } + } - enumChanged() { - let enumValue = this.input.value; - let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props; - this.setState({ - otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER - }); + multiSelectEnumChanged(enumValue) { + let { onEnumChange, onOtherChange } = this.props; + let selectedValues = enumValue.map(enumVal => { + return enumVal.value; + }); - let value = isMultiSelect ? [enumValue] : enumValue; - if (onEnumChange) { - onEnumChange(value); - } - if (onChange) { - onChange(value); - } - } + if (this.state.otherInputDisabled === false) { + selectedValues.shift(); + } else if (selectedValues.includes(i18n(other.OTHER))) { + selectedValues = [i18n(other.OTHER)]; + } - multiSelectEnumChanged(enumValue) { - let {onEnumChange, onOtherChange} = this.props; - let selectedValues = enumValue.map(enumVal => { - return enumVal.value; - }); + this.setState({ + otherInputDisabled: + !Boolean(onOtherChange) || + !selectedValues.includes(i18n(other.OTHER)) + }); + onEnumChange(selectedValues); + } - if (this.state.otherInputDisabled === false) { - selectedValues.shift(); - } - else if (selectedValues.includes(i18n(other.OTHER))) { - selectedValues = [i18n(other.OTHER)]; - } + changedOtherInput() { + let { onOtherChange } = this.props; + onOtherChange(this.otherValue.value); + } - this.setState({ - otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER)) - }); - onEnumChange(selectedValues); - } - - changedOtherInput() { - let {onOtherChange} = this.props; - onOtherChange(this.otherValue.value); - } - - componentDidUpdate() { - let {otherValue, selectedEnum, onInputChange, multiSelectedEnum} = this.props; - if (this.oldProps.otherValue !== otherValue - || this.oldProps.selectedEnum !== selectedEnum - || this.oldProps.multiSelectedEnum !== multiSelectedEnum) { - this.oldProps = { - otherValue, - selectedEnum, - multiSelectedEnum - }; - onInputChange(); - } - } - - static getTitleByName(values, name) { - for (let key of Object.keys(values)) { - let option = values[key].find(option => option.enum === name); - if (option) { - return option.title; - } - } - return name; - } + componentDidUpdate() { + let { + otherValue, + selectedEnum, + onInputChange, + multiSelectedEnum + } = this.props; + if ( + this.oldProps.otherValue !== otherValue || + this.oldProps.selectedEnum !== selectedEnum || + this.oldProps.multiSelectedEnum !== multiSelectedEnum + ) { + this.oldProps = { + otherValue, + selectedEnum, + multiSelectedEnum + }; + onInputChange(); + } + } + static getTitleByName(values, name) { + for (let key of Object.keys(values)) { + let option = values[key].find(option => option.enum === name); + if (option) { + return option.title; + } + } + return name; + } } export default InputOptions; diff --git a/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx index e440fcda69..3e0bb32ca9 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx @@ -22,114 +22,149 @@ import FormGroup from 'react-bootstrap/lib/FormGroup.js'; import FormControl from 'react-bootstrap/lib/FormControl.js'; class InputWrapper extends React.Component { - - state = { - value: this.props.value, - checked: this.props.checked, - selectedValues: [] - } - - render() { - const {label, hasError, validations = {}, isReadOnlyMode, value, onBlur, onKeyDown, type, disabled, checked, name} = this.props; - const {groupClassName, ...inputProps} = this.props; - return( - <FormGroup className={classNames('form-group', [groupClassName], {'required' : validations.required , 'has-error' : hasError})} > - {(label && (type !== 'checkbox' && type !== 'radio')) && <label className='control-label'>{label}</label>} - {(type === 'text' || type === 'number') && - <FormControl - bsClass={'form-control input-options-other'} - onChange={(e) => this.onChange(e)} - disabled={isReadOnlyMode || Boolean(disabled)} - onBlur={onBlur} - onKeyDown={onKeyDown} - value={value || ''} - ref={(input) => this.inputWrapper = input} - type={type} - data-test-id={this.props['data-test-id']}/>} - - {type === 'textarea' && - <FormControl - className='form-control input-options-other' - disabled={isReadOnlyMode || Boolean(disabled)} - value={value || ''} - onBlur={onBlur} - onKeyDown={onKeyDown} - componentClass={type} - onChange={(e) => this.onChange(e)} - data-test-id={this.props['data-test-id']}/>} - - {type === 'checkbox' && - <Checkbox - className={classNames({'required' : validations.required , 'has-error' : hasError})} - onChange={(e)=>this.onChangeCheckBox(e)} - disabled={isReadOnlyMode || Boolean(disabled)} - checked={value} - data-test-id={this.props['data-test-id']}>{label}</Checkbox>} - - {type === 'radio' && - <Radio name={name} - checked={checked} - disabled={isReadOnlyMode || Boolean(disabled)} - value={value} - ref={(input) => this.inputWrapper = input} - onChange={(isChecked)=>this.onChangeRadio(isChecked)} label={label} - data-test-id={this.props['data-test-id']} />} - {type === 'select' && - <FormControl onClick={ (e) => this.optionSelect(e) } - componentClass={type} - name={name} {...inputProps} - data-test-id={this.props['data-test-id']}/>} - - </FormGroup> - - ); - } - - getValue() { - return this.props.type !== 'select' ? this.state.value : this.state.selectedValues; - } - - getChecked() { - return this.state.checked; - } - - optionSelect(e) { - let selectedValues = []; - if (e.target.value) { - selectedValues.push(e.target.value); - } - this.setState({ - selectedValues - }); - } - - onChange(e) { - let {onChange} = this.props; - this.setState({ - value: e.target.value - }); - onChange(e.target.value); - } - - onChangeCheckBox(e) { - let {onChange} = this.props; - this.setState({ - checked: e.target.checked - }); - onChange(e.target.checked); - } - - onChangeRadio(isChecked) { - let {onChange} = this.props; - this.setState({ - checked: isChecked - }); - onChange(this.state.value); - } - - focus() { - ReactDOM.findDOMNode(this.inputWrapper).focus(); - } - + state = { + value: this.props.value, + checked: this.props.checked, + selectedValues: [] + }; + + render() { + const { + label, + hasError, + validations = {}, + isReadOnlyMode, + value, + onBlur, + onKeyDown, + type, + disabled, + checked, + name + } = this.props; + const { groupClassName, ...inputProps } = this.props; + return ( + <FormGroup + className={classNames('form-group', [groupClassName], { + required: validations.required, + 'has-error': hasError + })}> + {label && + (type !== 'checkbox' && type !== 'radio') && ( + <label className="control-label">{label}</label> + )} + {(type === 'text' || type === 'number') && ( + <FormControl + bsClass={'form-control input-options-other'} + onChange={e => this.onChange(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + onBlur={onBlur} + onKeyDown={onKeyDown} + value={value || ''} + ref={input => (this.inputWrapper = input)} + type={type} + data-test-id={this.props['data-test-id']} + /> + )} + + {type === 'textarea' && ( + <FormControl + className="form-control input-options-other" + disabled={isReadOnlyMode || Boolean(disabled)} + value={value || ''} + onBlur={onBlur} + onKeyDown={onKeyDown} + componentClass={type} + onChange={e => this.onChange(e)} + data-test-id={this.props['data-test-id']} + /> + )} + + {type === 'checkbox' && ( + <Checkbox + className={classNames({ + required: validations.required, + 'has-error': hasError + })} + onChange={e => this.onChangeCheckBox(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + checked={value} + data-test-id={this.props['data-test-id']}> + {label} + </Checkbox> + )} + + {type === 'radio' && ( + <Radio + name={name} + checked={checked} + disabled={isReadOnlyMode || Boolean(disabled)} + value={value} + ref={input => (this.inputWrapper = input)} + onChange={isChecked => this.onChangeRadio(isChecked)} + label={label} + data-test-id={this.props['data-test-id']} + /> + )} + {type === 'select' && ( + <FormControl + onClick={e => this.optionSelect(e)} + componentClass={type} + name={name} + {...inputProps} + data-test-id={this.props['data-test-id']} + /> + )} + </FormGroup> + ); + } + + getValue() { + return this.props.type !== 'select' + ? this.state.value + : this.state.selectedValues; + } + + getChecked() { + return this.state.checked; + } + + optionSelect(e) { + let selectedValues = []; + if (e.target.value) { + selectedValues.push(e.target.value); + } + this.setState({ + selectedValues + }); + } + + onChange(e) { + let { onChange } = this.props; + this.setState({ + value: e.target.value + }); + onChange(e.target.value); + } + + onChangeCheckBox(e) { + let { onChange } = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(e.target.checked); + } + + onChangeRadio(isChecked) { + let { onChange } = this.props; + this.setState({ + checked: isChecked + }); + onChange(this.state.value); + } + + focus() { + ReactDOM.findDOMNode(this.inputWrapper).focus(); + } } -export default InputWrapper; +export default InputWrapper; diff --git a/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx index 0982c133e6..429985a902 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx @@ -16,64 +16,76 @@ import React from 'react'; import PropTypes from 'prop-types'; import ReactDOM from 'react-dom'; -import {default as SDCTabs} from 'sdc-ui/lib/react/Tabs.js'; +import { default as SDCTabs } from 'sdc-ui/lib/react/Tabs.js'; import Overlay from 'react-bootstrap/lib/Overlay.js'; import Tooltip from 'react-bootstrap/lib/Tooltip.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; -export default -class Tabs extends React.Component { +export default class Tabs extends React.Component { + static propTypes = { + children: PropTypes.node + }; - static propTypes = { - children: PropTypes.node - }; + cloneTab(element) { + const { invalidTabs } = this.props; + return React.cloneElement(element, { + key: element.props.tabId, + className: + invalidTabs.indexOf(element.props.tabId) > -1 + ? 'invalid-tab' + : 'valid-tab' + }); + } - cloneTab(element) { - const {invalidTabs} = this.props; - return React.cloneElement( - element, - { - key: element.props.tabId, - className: invalidTabs.indexOf(element.props.tabId) > -1 ? 'invalid-tab' : 'valid-tab' - } - ); - } + showTabsError() { + const { invalidTabs } = this.props; + const showError = + (invalidTabs.length === 1 && + invalidTabs[0] !== this.props.activeTab) || + invalidTabs.length > 1; + return showError; + } - showTabsError() { - const {invalidTabs} = this.props; - const showError = ((invalidTabs.length === 1 && invalidTabs[0] !== this.props.activeTab) || (invalidTabs.length > 1)); - return showError; - } - - render() { - // eslint-disable-next-line no-unused-vars - let {invalidTabs, ...tabProps} = this.props; - return ( - <div> - <SDCTabs {...tabProps} ref='tabsList' id='tabsList' > - {this.props.children.map(element => this.cloneTab(element))} - </SDCTabs> - <Overlay - animation={false} - show={this.showTabsError()} - placement='bottom' - target={() => { - let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.sdc-tab-active):nth-of-type(n)'); - return target && target.offsetParent ? target : undefined; - } - } - container={() => { - let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.sdc-tab-active):nth-of-type(n)'); - return target && target.offsetParent ? target.offsetParent : this; - }}> - <Tooltip - id='error-some-tabs-contain-errors' - className='validation-error-message'> - {i18n('One or more tabs are invalid')} - </Tooltip> - </Overlay> - </div> - ); - } + render() { + // eslint-disable-next-line no-unused-vars + let { invalidTabs, ...tabProps } = this.props; + return ( + <div> + <SDCTabs {...tabProps} ref="tabsList" id="tabsList"> + {this.props.children.map(element => this.cloneTab(element))} + </SDCTabs> + <Overlay + animation={false} + show={this.showTabsError()} + placement="bottom" + target={() => { + let target = ReactDOM.findDOMNode( + this.refs.tabsList + ).querySelector( + 'ul > li.invalid-tab:not(.sdc-tab-active):nth-of-type(n)' + ); + return target && target.offsetParent + ? target + : undefined; + }} + container={() => { + let target = ReactDOM.findDOMNode( + this.refs.tabsList + ).querySelector( + 'ul > li.invalid-tab:not(.sdc-tab-active):nth-of-type(n)' + ); + return target && target.offsetParent + ? target.offsetParent + : this; + }}> + <Tooltip + id="error-some-tabs-contain-errors" + className="validation-error-message"> + {i18n('One or more tabs are invalid')} + </Tooltip> + </Overlay> + </div> + ); + } } diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx index 151d3fe859..e460f68a98 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx @@ -27,32 +27,63 @@ import Button from 'sdc-ui/lib/react/Button.js'; import SVGIcon from 'sdc-ui/lib/react/SVGIcon.js'; class ValidationButtons extends React.Component { + static propTypes = { + labledButtons: PropTypes.bool.isRequired, + isReadOnlyMode: PropTypes.bool, + submitButtonText: PropTypes.string, + cancelButtonText: PropTypes.string + }; - static propTypes = { - labledButtons: PropTypes.bool.isRequired, - isReadOnlyMode: PropTypes.bool, - submitButtonText: PropTypes.string, - cancelButtonText: PropTypes.string - }; + state = { + isValid: this.props.formValid + }; - state = { - isValid: this.props.formValid - }; - - render() { - let submitBtn = this.props.labledButtons ? this.props.submitButtonText ? this.props.submitButtonText : i18n('Save') : <SVGIcon className='check' name='check'/>; - let closeBtn = this.props.labledButtons ? this.props.cancelButtonText ? this.props.cancelButtonText : i18n('Cancel') : <SVGIcon className='close' name='close'/>; - return ( - <div className='validation-buttons'> - {!this.props.isReadOnlyMode ? - <div> - <Button type='submit' data-test-id='form-submit-button' disabled={!this.state.isValid}>{submitBtn}</Button> - <Button btnType='outline' type='reset' data-test-id='form-close-button'>{closeBtn}</Button> - </div> - : <Button btnType='outline' type='reset' data-test-id='form-close-button'>{i18n('Close')}</Button> - } - </div> - ); - } + render() { + let submitBtn = this.props.labledButtons ? ( + this.props.submitButtonText ? ( + this.props.submitButtonText + ) : ( + i18n('Save') + ) + ) : ( + <SVGIcon className="check" name="check" /> + ); + let closeBtn = this.props.labledButtons ? ( + this.props.cancelButtonText ? ( + this.props.cancelButtonText + ) : ( + i18n('Cancel') + ) + ) : ( + <SVGIcon className="close" name="close" /> + ); + return ( + <div className="validation-buttons"> + {!this.props.isReadOnlyMode ? ( + <div> + <Button + type="submit" + data-test-id="form-submit-button" + disabled={!this.state.isValid}> + {submitBtn} + </Button> + <Button + btnType="outline" + type="reset" + data-test-id="form-close-button"> + {closeBtn} + </Button> + </div> + ) : ( + <Button + btnType="outline" + type="reset" + data-test-id="form-close-button"> + {i18n('Close')} + </Button> + )} + </div> + ); + } } export default ValidationButtons; |