From 280f8015d06af1f41a3ef12e8300801c7a5e0d54 Mon Sep 17 00:00:00 2001 From: AviZi Date: Fri, 9 Jun 2017 02:39:56 +0300 Subject: [SDC-29] Amdocs OnBoard 1707 initial commit. Change-Id: Ie4d12a3f574008b792899b368a0902a8b46b5370 Signed-off-by: AviZi --- .../src/nfvo-components/SubmitErrorResponse.jsx | 175 ++++--- .../nfvo-components/activity-log/ActivityLog.js | 27 ++ .../activity-log/ActivityLogActionHelper.js | 31 ++ .../activity-log/ActivityLogConstants.js | 23 + .../activity-log/ActivityLogReducer.js | 25 + .../activity-log/ActivityLogView.jsx | 124 +++++ .../confirmations/ConfirmationModalView.jsx | 53 --- .../src/nfvo-components/editor/TabulatedEditor.jsx | 31 +- openecomp-ui/src/nfvo-components/grid/GridItem.jsx | 26 ++ .../src/nfvo-components/grid/GridSection.jsx | 33 ++ openecomp-ui/src/nfvo-components/icon/Icon.jsx | 45 ++ openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx | 54 +++ .../src/nfvo-components/icon/SVGIcon.stories.js | 50 ++ .../src/nfvo-components/input/ExpandableInput.jsx | 140 +++--- .../src/nfvo-components/input/SelectInput.jsx | 18 +- .../src/nfvo-components/input/ToggleInput.jsx | 15 + .../input/dualListbox/DualListboxView.jsx | 67 ++- .../input/inputOptions/InputOptions.jsx | 55 ++- .../src/nfvo-components/input/validation/Form.jsx | 114 +++++ .../src/nfvo-components/input/validation/Input.jsx | 180 ++++++++ .../input/validation/InputOptions.jsx | 279 +++++++++++ .../input/validation/InputWrapper.jsx | 134 ++++++ .../src/nfvo-components/input/validation/Tabs.jsx | 79 ++++ .../input/validation/ValidationButtons.jsx | 21 +- .../input/validation/ValidationForm.jsx | 200 -------- .../input/validation/ValidationInput.jsx | 509 --------------------- .../input/validation/ValidationTab.jsx | 107 ----- .../input/validation/ValidationTabs.jsx | 72 --- .../listEditor/ListEditorItemView.jsx | 38 +- .../listEditor/ListEditorItemViewField.jsx | 24 + .../nfvo-components/listEditor/ListEditorView.jsx | 99 ++-- .../listEditor/listEditor.stories.js | 60 +++ openecomp-ui/src/nfvo-components/loader/Loader.jsx | 15 + .../src/nfvo-components/loader/LoaderConstants.js | 21 +- .../src/nfvo-components/loader/LoaderReducer.js | 21 +- .../src/nfvo-components/modal/GlobalModal.js | 120 +++++ .../nfvo-components/modal/GlobalModalConstants.js | 33 ++ .../nfvo-components/modal/GlobalModalReducer.js | 50 ++ openecomp-ui/src/nfvo-components/modal/Modal.jsx | 15 + .../notifications/NotificationConstants.js | 29 -- .../notifications/NotificationModal.jsx | 100 ---- .../notifications/NotificationReducer.js | 51 --- .../nfvo-components/panel/NavigationSideBar.jsx | 125 +++-- .../src/nfvo-components/panel/SlidePanel.jsx | 109 ----- .../panel/versionController/VersionController.jsx | 245 +++++----- .../VersionControllerConstants.js | 29 +- .../versionController/VersionControllerUtils.js | 39 +- .../nfvo-components/progressBar/ProgressBar.jsx | 15 + .../nfvo-components/table/SelectActionTable.jsx | 29 ++ .../table/SelectActionTableCell.jsx | 20 + .../nfvo-components/table/SelectActionTableRow.jsx | 30 ++ 51 files changed, 2350 insertions(+), 1654 deletions(-) create mode 100644 openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js create mode 100644 openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js create mode 100644 openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js create mode 100644 openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js create mode 100644 openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx delete mode 100644 openecomp-ui/src/nfvo-components/confirmations/ConfirmationModalView.jsx create mode 100644 openecomp-ui/src/nfvo-components/grid/GridItem.jsx create mode 100644 openecomp-ui/src/nfvo-components/grid/GridSection.jsx create mode 100644 openecomp-ui/src/nfvo-components/icon/Icon.jsx create mode 100644 openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx create mode 100644 openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js create mode 100644 openecomp-ui/src/nfvo-components/input/validation/Form.jsx create mode 100644 openecomp-ui/src/nfvo-components/input/validation/Input.jsx create mode 100644 openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx create mode 100644 openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx create mode 100644 openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx delete mode 100644 openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx delete mode 100644 openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx delete mode 100644 openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx delete mode 100644 openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx create mode 100644 openecomp-ui/src/nfvo-components/listEditor/ListEditorItemViewField.jsx create mode 100644 openecomp-ui/src/nfvo-components/listEditor/listEditor.stories.js create mode 100644 openecomp-ui/src/nfvo-components/modal/GlobalModal.js create mode 100644 openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js create mode 100644 openecomp-ui/src/nfvo-components/modal/GlobalModalReducer.js delete mode 100644 openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js delete mode 100644 openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx delete mode 100644 openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js delete mode 100644 openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx create mode 100644 openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx create mode 100644 openecomp-ui/src/nfvo-components/table/SelectActionTableCell.jsx create mode 100644 openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx (limited to 'openecomp-ui/src/nfvo-components') diff --git a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx index f2ec1582f3..0759f2c28d 100644 --- a/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx +++ b/openecomp-ui/src/nfvo-components/SubmitErrorResponse.jsx @@ -1,9 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React, {Component} from 'react'; import ListGroupItem from 'react-bootstrap/lib/ListGroupItem.js'; -import ListGroup from 'react-bootstrap/lib/ListGroup.js'; -import Panel from 'react-bootstrap/lib/Panel.js'; import i18n from 'nfvo-utils/i18n/i18n.js'; - +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Icon from 'nfvo-components/icon/Icon.jsx'; +import {Collapse} from 'react-bootstrap'; /** * parsing and showing the following Java Response object * @@ -31,103 +46,117 @@ class SubmitErrorResponse extends Component { render() { - let {validationResponse} = this.props; + let {validationResponse : {vspErrors, licensingDataErrors, questionnaireValidationResult, uploadDataErrors}} = this.props; return (
- {validationResponse.vspErrors && this.renderVspErrors(validationResponse.vspErrors)} - {validationResponse.licensingDataErrors && this.renderVspErrors(validationResponse.licensingDataErrors)} - {validationResponse.compilationErrors && this.renderCompilationErrors(validationResponse.compilationErrors)} - {validationResponse.uploadDataErrors && this.renderUploadDataErrors(validationResponse.uploadDataErrors)} - {validationResponse.questionnaireValidationResult && this.renderQuestionnaireValidationResult(validationResponse.questionnaireValidationResult)} + {vspErrors && this.renderVspErrors(vspErrors)} + {licensingDataErrors && this.renderVspErrors(licensingDataErrors)} + {questionnaireValidationResult && this.renderComponentsErrors(questionnaireValidationResult)} + {uploadDataErrors && this.renderUploadDataErrors(uploadDataErrors)}
); } - renderVspErrors(vspErrors) { + renderVspErrors(errors) { return ( - {this.parseErrorCodeCollection(vspErrors)} + +
+ {errors.length && errors.map(error=>{return ();})} +
+
); } - renderLicensingDataErrors(licensingDataErrors) { + + renderComponentsErrors(errors) { return ( - {this.parseErrorCodeCollection(licensingDataErrors)} - + +
+ {errors.validationData.length && errors.validationData.map(item =>{ return ();})} +
+
); } renderUploadDataErrors(uploadDataErrors) { return ( - {this.parseMapOfErrorMessagesList(uploadDataErrors)} - + +
+ +
+
); } +} - renderCompilationErrors(compilationErrors) { - return ( - {this.parseMapOfErrorMessagesList(compilationErrors)} - - ); - } - parseErrorCodeCollection(errors) { - return ( - {errors.map(error => - -
{i18n('Category: ')}{error.category}
-
{i18n('Message: ')}{error.message}
-
- )}
- ); - } +const ComponentError = ({item}) => { + let i = 0; + return ( +
+
{item.entityName}
+ {item.errors.map(error => {return();})} +
+ ); +}; - parseMapOfErrorMessagesList(errorMap) { - return ( - - {Object.keys(errorMap).map(errorStringKey => - - {errorMap[errorStringKey].map(error => - -
{i18n('Level: ')}{error.level}
-
{i18n('Message: ')}{error.message}
-
- )}
-
- )} -
- ); +function* entries(obj) { + for (let key of Object.keys(obj)) { + yield {header: key, list: obj[key]}; } +} +const UploadErrorList = ({items}) => { + let generator = entries(items); + + let errors = []; + let i = 0; + for (let item of generator) {errors.push( +
+
{item.header}
+ {item.list.map(error => )} +
+ );} + return ( +
+ {errors} +
+ ); +}; + +class ErrorBlock extends React.Component { + state = { + collapsed: false + }; - renderQuestionnaireValidationResult(questionnaireValidationResult) { - if (!questionnaireValidationResult.valid) { - return this.parseAndRenderCompositionEntityValidationData(questionnaireValidationResult.validationData); - } - } - - parseAndRenderCompositionEntityValidationData(validationData) { - let {entityType, entityId, errors = [], subEntitiesValidationData = []} = validationData; + render() { + let {errorType, children} = this.props; return ( - - - {errors.map(error => - -
{error}
-
- )}
- {subEntitiesValidationData.map(subValidationData => this.parseAndRenderCompositionEntityValidationData(subValidationData))} -
-
+
+ {this.setState({collapsed: !this.state.collapsed});}} errorType={errorType}/> + + {children} + +
); } - - } +const ErrorHeader = ({errorType, collapsed, onClick}) => { + return( +
+ + {errorType} +
+ ); +}; + +const ErrorMessage = ({error, warning}) => { + return ( + + + + ); +}; + export default SubmitErrorResponse; diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js new file mode 100644 index 0000000000..f7354f96e2 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLog.js @@ -0,0 +1,27 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import {connect} from 'react-redux'; +import ActivityLogView from './ActivityLogView.jsx'; + +export const mapStateToProps = ({licenseModel: {activityLog}}) => { + + let activities = activityLog; + return { + activities + }; +}; + +export default connect(mapStateToProps, undefined, null, {withRef: true})(ActivityLogView); diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js new file mode 100644 index 0000000000..01a27abbc5 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogActionHelper.js @@ -0,0 +1,31 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import RestAPIUtil from 'nfvo-utils/RestAPIUtil.js'; +import Configuration from 'sdc-app/config/Configuration.js'; +import ActivityLogConstants from './ActivityLogConstants.js'; + + +function baseUrl(itemId, versionId) { + const restPrefix = Configuration.get('restPrefix'); + return `${restPrefix}/v1.0/activity-logs/${itemId}/versions/${versionId}`; +} + +export default { + + fetchActivityLog(dispatch, {itemId, versionId}){ + return RestAPIUtil.fetch(baseUrl(itemId, versionId)).then(response => dispatch({type: ActivityLogConstants.ACTIVITY_LOG_UPDATED, response})); + } +}; diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js new file mode 100644 index 0000000000..69faf7cbb6 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogConstants.js @@ -0,0 +1,23 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export default keyMirror({ + + ACTIVITY_LOG_UPDATED: null + +}); + diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js new file mode 100644 index 0000000000..fc3dfa1515 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogReducer.js @@ -0,0 +1,25 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import ActivityLogConstants from './ActivityLogConstants.js'; + +export default (state = [], action) => { + switch (action.type) { + case ActivityLogConstants.ACTIVITY_LOG_UPDATED: + return [...action.response.results]; + } + + return state; +}; diff --git a/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx new file mode 100644 index 0000000000..6ff3c806a8 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/activity-log/ActivityLogView.jsx @@ -0,0 +1,124 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {Component} from 'react'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +function ActivityLogSortableCellHeader({isHeader, data, isDes, onSort}) { + if (isHeader) { + return ( + + {data} + + + ); + } + return ( + + {i18n.dateNormal(data, { + year: 'numeric', month: 'numeric', day: 'numeric' + })} + {i18n.dateNormal(data, { + hour: 'numeric', minute: 'numeric', + hour12: true + })} + + ); +} + +function ActivityLogStatus({status, isHeader}) { + if (isHeader) { + return {status}; + } + let {message, success} = status; + return ( + + {`${success ? i18n('Success') : i18n('Failure')}`} + {success && } + {!success && +
{message}
+ }> + {'?'} +
} +
+ ); +} + +export function ActivityListItem({activity, isHeader, isDes, onSort}) { + let {type, timestamp, comment, user, status} = activity; + return ( +
  • +
    +
    {type}
    +
    {comment}
    +
    {user}
    +
    +
  • + ); +} + +class ActivityLogView extends Component { + + state = { + localFilter: '', + sortDescending: true + }; + + render() { + return ( +
    + this.setState({localFilter: filter})}> + this.setState({sortDescending: !this.state.sortDescending})}/> + {this.sortActivities(this.filterActivities(), this.state.sortDescending).map(activity => )} + +
    + ); + } + + filterActivities() { + let {activities} = this.props; + let {localFilter} = this.state; + if (localFilter.trim()) { + const filter = new RegExp(escape(localFilter), 'i'); + return activities.filter(({user = '', comment = '', type = ''}) => escape(user).match(filter) || escape(comment).match(filter) || escape(type).match(filter)); + } + else { + return activities; + } + } + + sortActivities(activities) { + if (this.state.sortDescending) { + return activities.sort((a, b) => a.timestamp - b.timestamp); + } + else { + return activities.reverse(); + } + } + +} + +export default ActivityLogView; diff --git a/openecomp-ui/src/nfvo-components/confirmations/ConfirmationModalView.jsx b/openecomp-ui/src/nfvo-components/confirmations/ConfirmationModalView.jsx deleted file mode 100644 index cc971c608c..0000000000 --- a/openecomp-ui/src/nfvo-components/confirmations/ConfirmationModalView.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React from 'react'; -import Button from 'react-bootstrap/lib/Button.js'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Modal from 'nfvo-components/modal/Modal.jsx'; - -let typeClass = { - 'default': 'primary', - error: 'danger', - warning: 'warning', - success: 'success' -}; - - -class ConfirmationModalView extends React.Component { - - static propTypes = { - show: React.PropTypes.bool, - type: React.PropTypes.oneOf(['default', 'error', 'warning', 'success']), - msg: React.PropTypes.node, - title: React.PropTypes.string, - confirmationDetails: React.PropTypes.object, - confirmationButtonText: React.PropTypes.string, - - }; - - static defaultProps = { - show: false, - type: 'warning', - title: 'Warning', - msg: '', - confirmationButtonText: i18n('Delete') - }; - - render() { - let {title, type, msg, show, confirmationButtonText} = this.props; - - return( - - - {title} - - {msg} - - - - - - ); - }; -} - -export default ConfirmationModalView; diff --git a/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx b/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx index 4a106b5ff4..2a0b7d4d2a 100644 --- a/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx +++ b/openecomp-ui/src/nfvo-components/editor/TabulatedEditor.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import classnames from 'classnames'; @@ -7,10 +22,17 @@ import NavigationSideBar from 'nfvo-components/panel/NavigationSideBar.jsx'; export default class TabulatedEditor extends React.Component { render() { - const {versionControllerProps, navigationBarProps, onToggle, onVersionSwitching, onCreate, onSave, onClose, onVersionControllerAction, onNavigate, children} = this.props; + const {navigationBarProps, onToggle, onVersionSwitching, onCreate, onSave, onClose, onVersionControllerAction, onNavigate, children, meta} = this.props; + let {versionControllerProps} = this.props; const {className = ''} = React.Children.only(children).props; const child = this.prepareChild(); + if(onClose) { + versionControllerProps = { + ...versionControllerProps, + onClose: () => onClose(versionControllerProps) + }; + } return (
    @@ -19,11 +41,10 @@ export default class TabulatedEditor extends React.Component {
    onVersionSwitching(version)} - callVCAction={onVersionControllerAction} + onVersionSwitching={version => onVersionSwitching(version, meta)} + callVCAction={(action, version) => onVersionControllerAction(action, version, meta)} onCreate={onCreate && this.handleCreate} - onSave={onSave && this.handleSave} - onClose={() => onClose(versionControllerProps)}/> + onSave={onSave && this.handleSave}/>
    { child diff --git a/openecomp-ui/src/nfvo-components/grid/GridItem.jsx b/openecomp-ui/src/nfvo-components/grid/GridItem.jsx new file mode 100644 index 0000000000..8819ab78a3 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/grid/GridItem.jsx @@ -0,0 +1,26 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; + +const GridItem = ({colSpan = 1, children, stretch = false}) => ( +
    +
    + {children} +
    +
    +); + +export default GridItem; diff --git a/openecomp-ui/src/nfvo-components/grid/GridSection.jsx b/openecomp-ui/src/nfvo-components/grid/GridSection.jsx new file mode 100644 index 0000000000..175b3ee082 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/grid/GridSection.jsx @@ -0,0 +1,33 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; + +const GridSection = ({title, children, titleClassName}) => { + return ( +
    + {title &&
    {title}
    } +
    + {children} +
    +
    + ); +}; + +GridSection.propTypes = { + title: React.PropTypes.string, +}; + +export default GridSection; diff --git a/openecomp-ui/src/nfvo-components/icon/Icon.jsx b/openecomp-ui/src/nfvo-components/icon/Icon.jsx new file mode 100644 index 0000000000..125577664b --- /dev/null +++ b/openecomp-ui/src/nfvo-components/icon/Icon.jsx @@ -0,0 +1,45 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, { Component, PropTypes } from 'react'; + + +export default class Icon extends Component { + + static propTypes = { + image: PropTypes.string.isRequired, + onClick: PropTypes.func, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + className: PropTypes.string, + iconClassName: PropTypes.string + }; + + static defaultProps = { + label: '', + className: '', + iconClassName: '' + }; + + render() { + let {image, onClick, label, className, iconClassName, ...other} = this.props; + let classes = `icon-component ${className} ${onClick ? 'clickable' : ''}`; + return ( +
    + + {label} +
    + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx b/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx new file mode 100644 index 0000000000..dd165fb52c --- /dev/null +++ b/openecomp-ui/src/nfvo-components/icon/SVGIcon.jsx @@ -0,0 +1,54 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React, {PropTypes} from 'react'; +import Configuration from 'sdc-app/config/Configuration.js'; + +export default class SVGIcon extends React.Component { + + static propTypes = { + name: PropTypes.string.isRequired, + onClick: PropTypes.func, + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + labelPosition: PropTypes.string, + className: PropTypes.string, + iconClassName: PropTypes.string, + labelClassName: PropTypes.string + }; + + static defaultProps = { + name: '', + label: '', + className: '', + iconClassName: '', + labelClassName: '', + labelPosition: 'bottom' + }; + + render() { + let {name, onClick, label, className, iconClassName, labelClassName, labelPosition, ...other} = this.props; + let classes = `svg-icon-wrapper ${className} ${onClick ? 'clickable' : ''} ${labelPosition}`; + + return ( +
    + + + + {label && {label}} +
    + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js b/openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js new file mode 100644 index 0000000000..6675670cea --- /dev/null +++ b/openecomp-ui/src/nfvo-components/icon/SVGIcon.stories.js @@ -0,0 +1,50 @@ +import React from 'react'; +import {storiesOf, action} from '@kadira/storybook'; +import {select, text, withKnobs} from '@kadira/storybook-addon-knobs'; +import SVGIcon from './SVGIcon.jsx'; + +const stories = storiesOf('SVGIcon', module); + +const iconNames = ['locked', + 'pencil', + 'plus-circle', + 'plus', + 'search', + 'sliders', + 'trash-o', + 'unlocked', + 'vendor', + 'version-controller-lock-closed', + 'version-controller-lock-open', + 'version-controller-revert', + 'version-controller-save', + 'version-controller-submit', + 'vlm', + 'vsp' ]; + +function colorChanger() { + return {fill: text('Color', '')}; +} + +function iconName() { + return select('Icon name' , iconNames, iconNames[0]); +} + +stories.addDecorator(withKnobs); + +stories + .add('icon', () => { + return ( + + ); + }) + .add('icon with label', () => { + return ( + + ); + }) + .add('locked clickable', () => { + return ( + + ); + }); \ No newline at end of file diff --git a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx index 3ac3fcad28..e2ee40fcd2 100644 --- a/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/ExpandableInput.jsx @@ -1,77 +1,115 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import classnames from 'classnames'; -import Input from 'react-bootstrap/lib/Input'; +import ReactDOM from 'react-dom'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Input from 'nfvo-components/input/validation/InputWrapper.jsx'; +const ExpandableInputClosed = ({iconType, onClick}) => ( + +); -class ExpandableInput extends React.Component { - constructor(props){ - super(props); - this.state = {showInput: false, value: ''}; - this.toggleInput = this.toggleInput.bind(this); - this.handleFocus = this.handleFocus.bind(this); - this.handleInput = this.handleInput.bind(this); - this.handleClose = this.handleClose.bind(this); +class ExpandableInputOpened extends React.Component { + componentDidMount(){ + this.rawDomNode = ReactDOM.findDOMNode(this.searchInputNode.inputWrapper); + this.rawDomNode.focus(); } - toggleInput(){ - if (!this.state.showInput){ - this.searchInputNode.refs.input.focus(); - } else { - this.setState({showInput: false}); + componentWillReceiveProps(newProps){ + if (!newProps.value){ + if (!(document.activeElement === this.rawDomNode)){ + this.props.handleBlur(); + } } } - handleInput(e){ - let {onChange} = this.props; + handleClose(){ + this.props.onChange(''); + this.rawDomNode.focus(); + } - this.setState({value: e.target.value}); - onChange(e); + handleKeyDown(e){ + if (e.key === 'Escape'){ + e.preventDefault(); + if (this.props.value) { + this.handleClose(); + } else { + this.rawDomNode.blur(); + } + }; } - handleClose(){ - this.handleInput({target: {value: ''}}); - this.searchInputNode.refs.input.focus(); + render() { + let {iconType, value, onChange, handleBlur} = this.props; + return ( +
    + this.searchInputNode = input} + className='expandable-active' + groupClassName='expandable-input-control' + onChange={e => onChange(e)} + onKeyDown={e => this.handleKeyDown(e)} + onBlur={handleBlur}/> + {value && this.handleClose()} name='close' />} + {!value && } +
    + ); } +} + +class ExpandableInput extends React.Component { - handleFocus(){ - if (!this.state.showInput){ - this.setState({showInput: true}); + static propTypes = { + iconType: React.PropTypes.string, + onChange: React.PropTypes.func, + value: React.PropTypes.string + }; + + state = {showInput: false}; + + closeInput(){ + if (!this.props.value) { + this.setState({showInput: false}); } } getValue(){ - return this.state.value; + return this.props.value; } render(){ - let {iconType} = this.props; - - let inputClasses = classnames({ - 'expandable-active': this.state.showInput, - 'expandable-not-active': !this.state.showInput - }); - - let iconClasses = classnames( - 'expandable-icon', - {'expandable-icon-active': this.state.showInput} - ); - + let {iconType, value, onChange = false} = this.props; return ( -
    - this.searchInputNode = input} - className={inputClasses} - groupClassName='expandable-input-control' - onChange={e => this.handleInput(e)} - onFocus={this.handleFocus}/> - {this.state.showInput && this.state.value && } - {!this.state.value && } +
    + {this.state.showInput && + this.handleKeyDown(e)} + handleBlur={() => this.closeInput()}/> + } + {!this.state.showInput && this.setState({showInput: true})} />}
    - ); + ); } } + export default ExpandableInput; diff --git a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx index 1036ac41c3..03c727379e 100644 --- a/openecomp-ui/src/nfvo-components/input/SelectInput.jsx +++ b/openecomp-ui/src/nfvo-components/input/SelectInput.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ /** * The HTML structure here is aligned with bootstrap HTML structure for form elements. * In this way we have proper styling and it is aligned with other form elements on screen. @@ -20,8 +35,9 @@ class SelectInput extends Component { render() { let {label, value, ...other} = this.props; + const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; return ( -
    +
    {label && } - + +
    this.onSelectItems(event.target.selectedOptions)} groupClassName='dual-list-box-multi-select' type='select' name='dual-list-box-multi-select' - {...props}> + 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 && } {unMatchedItems.map(item => this.renderOption(item.id, item.name))} @@ -92,6 +106,11 @@ class DualListboxView extends React.Component { ); } + onSelectItems(selectedOptions) { + let selectedValues = Object.keys(selectedOptions).map((k) => selectedOptions[k].value); + this.setState({selectedValues}); + } + renderOption(value, name) { return (); } @@ -107,17 +126,19 @@ class DualListboxView extends React.Component { ); } - renderOperationBarButton(onClick, fontAwesomeIconName){ - return (
    ); + renderOperationBarButton(onClick, iconName){ + return (
    ); } addToSelectedList() { - this.props.onChange(this.props.selectedValuesList.concat(this.refs.availableValues.getValue())); + this.props.onChange(this.props.selectedValuesList.concat(this.state.selectedValues)); + this.setState({selectedValues: []}); } removeFromSelectedList() { - const selectedValues = this.refs.selectedValues.getValue(); + const selectedValues = this.state.selectedValues; this.props.onChange(this.props.selectedValuesList.filter(value => !selectedValues.find(selectedValue => selectedValue === value))); + this.setState({selectedValues: []}); } addAllToSelectedList() { diff --git a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx index 5daaffea41..e8aadc4357 100644 --- a/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx +++ b/openecomp-ui/src/nfvo-components/input/inputOptions/InputOptions.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import classNames from 'classnames'; @@ -13,15 +28,21 @@ class InputOptions extends React.Component { title: React.PropTypes.string })), isEnabledOther: React.PropTypes.bool, - title: React.PropTypes.string, + label: React.PropTypes.string, selectedValue: React.PropTypes.string, - multiSelectedEnum: React.PropTypes.array, + multiSelectedEnum: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.array + ]), selectedEnum: React.PropTypes.string, otherValue: React.PropTypes.string, onEnumChange: React.PropTypes.func, onOtherChange: React.PropTypes.func, + onBlur: React.PropTypes.func, isRequired: React.PropTypes.bool, - isMultiSelect: React.PropTypes.bool + isMultiSelect: React.PropTypes.bool, + hasError: React.PropTypes.bool, + disabled: React.PropTypes.bool }; @@ -41,7 +62,7 @@ class InputOptions extends React.Component { render() { let {label, isRequired, values, otherValue, onOtherChange, isMultiSelect, onBlur, multiSelectedEnum, selectedEnum, hasError, validations, children} = this.props; - + const dataTestId = this.props['data-test-id'] ? {'data-test-id': this.props['data-test-id']} : {}; let currentMultiSelectedEnum = []; let currentSelectedEnum = ''; let {otherInputDisabled} = this.state; @@ -54,14 +75,18 @@ class InputOptions extends React.Component { else if(selectedEnum){ currentSelectedEnum = selectedEnum; } + if (!onBlur) { + onBlur = () => {}; + } let isReadOnlyMode = this.context.isReadOnlyMode; return( -
    +
    {label && } {isMultiSelect && otherInputDisabled ? onBlur()} disabled={isReadOnlyMode || Boolean(this.props.disabled)} onChange={ value => this.enumChanged(value)} type='select'> - {values && values.length && values.map(val => this.renderOptions(val))} + {children || (values && values.length && values.map((val, index) => this.renderOptions(val, index)))} {onOtherChange && } - {children} {!otherInputDisabled &&
    } @@ -104,9 +129,9 @@ class InputOptions extends React.Component { ); } - renderOptions(val){ - return( - + renderOptions(val, index){ + return ( + ); } @@ -154,9 +179,9 @@ class InputOptions extends React.Component { enumChanged() { let enumValue = this.refs._myInput.value; - let {onEnumChange, isMultiSelect, onChange} = this.props; + let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props; this.setState({ - otherInputDisabled: enumValue !== other.OTHER + otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER }); let value = isMultiSelect ? [enumValue] : enumValue; @@ -169,7 +194,7 @@ class InputOptions extends React.Component { } multiSelectEnumChanged(enumValue) { - let {onEnumChange} = this.props; + let {onEnumChange, onOtherChange} = this.props; let selectedValues = enumValue.map(enumVal => { return enumVal.value; }); @@ -182,7 +207,7 @@ class InputOptions extends React.Component { } this.setState({ - otherInputDisabled: !selectedValues.includes(i18n(other.OTHER)) + otherInputDisabled: !Boolean(onOtherChange) || !selectedValues.includes(i18n(other.OTHER)) }); onEnumChange(selectedValues); } diff --git a/openecomp-ui/src/nfvo-components/input/validation/Form.jsx b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx new file mode 100644 index 0000000000..47922f86a0 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/Form.jsx @@ -0,0 +1,114 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import ValidationButtons from './ValidationButtons.jsx'; + +class Form extends React.Component { + + static defaultProps = { + hasButtons : true, + onSubmit : null, + onReset : null, + labledButtons: true, + onValidChange : null, + isValid: true + }; + + static propTypes = { + isValid : React.PropTypes.bool, + formReady : React.PropTypes.bool, + isReadOnlyMode : React.PropTypes.bool, + hasButtons : React.PropTypes.bool, + onSubmit : React.PropTypes.func, + onReset : React.PropTypes.func, + labledButtons: React.PropTypes.bool, + onValidChange : React.PropTypes.func, + onValidityChanged: React.PropTypes.func, + onValidateForm: React.PropTypes.func + }; + + constructor(props) { + super(props); + } + + + render() { + // eslint-disable-next-line no-unused-vars + let {isValid, formReady, onValidateForm, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, onDataChanged, children, ...formProps} = this.props; + return ( +
    this.form = form} onSubmit={event => this.handleFormValidation(event)}> +
    +
    + {children} +
    +
    + {hasButtons && this.buttons = buttons} isReadOnlyMode={isReadOnlyMode}/>} + + ); + } + + 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 default Form; diff --git a/openecomp-ui/src/nfvo-components/input/validation/Input.jsx b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx new file mode 100644 index 0000000000..59c35d7993 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/Input.jsx @@ -0,0 +1,180 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import classNames from 'classnames'; +import Checkbox from 'react-bootstrap/lib/Checkbox.js'; +import Radio from 'react-bootstrap/lib/Radio.js'; +import FormGroup from 'react-bootstrap/lib/FormGroup.js'; +import FormControl from 'react-bootstrap/lib/FormControl.js'; +import Overlay from 'react-bootstrap/lib/Overlay.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +class Input extends React.Component { + + state = { + value: this.props.value, + checked: this.props.checked, + selectedValues: [] + } + + 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, ...inputProps} = this.props; + let wrapperClassName = (type !== 'radio') ? 'validation-input-wrapper' : 'form-group'; + if (disabled) { + wrapperClassName += ' disabled'; + } + return( +
    + + {(label && (type !== 'checkbox' && type !== 'radio')) && } + {(type === 'text' || type === 'number') && + 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 === 'textarea' && + this.onChange(e)} + inputRef={(input) => this.input = input} + data-test-id={this.props['data-test-id']}/>} + + {type === 'checkbox' && + this.onChangeCheckBox(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + checked={value} + data-test-id={this.props['data-test-id']}>{label}} + + {type === 'radio' && + this.onChangeRadio(e)} + data-test-id={this.props['data-test-id']}>{label}} + {type === 'select' && + this.optionSelect(e) } + componentClass={type} + inputRef={(input) => this.input = input} + name={name} {...inputProps} + data-test-id={this.props['data-test-id']}/>} + + { this.renderErrorOverlay() } +
    + ); + } + + 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) { + const {onChange, type} = this.props; + let value = e.target.value; + if (type === 'number') { + value = Number(value); + } + this.setState({ + value + }); + onChange(value); + } + + onChangeCheckBox(e) { + let {onChange} = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(e.target.checked); + } + + onChangeRadio(e) { + let {onChange} = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(this.state.value); + } + + focus() { + ReactDOM.findDOMNode(this.input).focus(); + } + + 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 === 'password') { + position = 'bottom'; + } + + return ( + { + let target = ReactDOM.findDOMNode(this.input); + return target.offsetParent ? target : undefined; + }} + container={this}> + + {errorText} + + + ); + } + +} +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 new file mode 100644 index 0000000000..6e54254eb0 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/InputOptions.jsx @@ -0,0 +1,279 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import classNames from 'classnames'; +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'}; + +class InputOptions extends React.Component { + + static propTypes = { + values: React.PropTypes.arrayOf(React.PropTypes.shape({ + enum: React.PropTypes.string, + title: React.PropTypes.string + })), + isEnabledOther: React.PropTypes.bool, + label: React.PropTypes.string, + selectedValue: React.PropTypes.string, + multiSelectedEnum: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.array + ]), + selectedEnum: React.PropTypes.string, + otherValue: React.PropTypes.string, + overlayPos: React.PropTypes.string, + onEnumChange: React.PropTypes.func, + onOtherChange: React.PropTypes.func, + onBlur: React.PropTypes.func, + isRequired: React.PropTypes.bool, + isMultiSelect: React.PropTypes.bool, + isValid: React.PropTypes.bool, + disabled: React.PropTypes.bool + }; + + state = { + otherInputDisabled: !this.props.otherValue + }; + + 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} = this.state; + if (isMultiSelect) { + currentMultiSelectedEnum = multiSelectedEnum; + if(!otherInputDisabled) { + currentSelectedEnum = multiSelectedEnum ? multiSelectedEnum.toString() : undefined; + } + } + else if(selectedEnum){ + currentSelectedEnum = selectedEnum; + } + if (!onBlur) { + onBlur = () => {}; + } + + return( +
    +
    + {label && } + {isMultiSelect && otherInputDisabled ? + 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 && } + + + {!otherInputDisabled &&
    } + this.otherValue = otherValue} + style={{'display' : otherInputDisabled ? 'none' : 'block'}} + disabled={isReadOnlyMode || Boolean(this.props.disabled)} + value={otherValue || ''} + onBlur={() => onBlur()} + onChange={() => this.changedOtherInput()}/> +
    + } +
    + { this.renderErrorOverlay() } +
    + ); + } + + renderOptions(val, index){ + return ( + + ); + } + + + 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; + + if (overlayPos) { + position = overlayPos; + } + else if (type === 'text' + || type === 'email' + || type === 'number' + || type === 'password') { + position = 'bottom'; + } + + return ( + { + let {otherInputDisabled} = this.state; + let target = otherInputDisabled ? ReactDOM.findDOMNode(this.input) : ReactDOM.findDOMNode(this.otherValue); + return target.offsetParent ? target : undefined; + }} + container={this}> + + {errorText} + + + ); + } + + getValue() { + let res = ''; + let {isMultiSelect} = this.props; + let {otherInputDisabled} = this.state; + + if (otherInputDisabled) { + res = isMultiSelect ? this.input.getValue() : this.input.value; + } else { + res = this.otherValue.value; + } + return res; + } + + enumChanged() { + let enumValue = this.input.value; + let {onEnumChange, onOtherChange, isMultiSelect, onChange} = this.props; + this.setState({ + otherInputDisabled: !Boolean(onOtherChange) || enumValue !== other.OTHER + }); + + let value = isMultiSelect ? [enumValue] : enumValue; + if (onEnumChange) { + onEnumChange(value); + } + if (onChange) { + onChange(value); + } + } + + multiSelectEnumChanged(enumValue) { + let {onEnumChange, onOtherChange} = this.props; + let selectedValues = enumValue.map(enumVal => { + return enumVal.value; + }); + + if (this.state.otherInputDisabled === false) { + selectedValues.shift(); + } + else if (selectedValues.includes(i18n(other.OTHER))) { + selectedValues = [i18n(other.OTHER)]; + } + + 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; + } + +} + +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 new file mode 100644 index 0000000000..5ca716cc20 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/InputWrapper.jsx @@ -0,0 +1,134 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import classNames from 'classnames'; +import Checkbox from 'react-bootstrap/lib/Checkbox.js'; +import Radio from 'react-bootstrap/lib/Radio.js'; +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( + + {(label && (type !== 'checkbox' && type !== 'radio')) && } + {(type === 'text' || type === 'number') && + 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' && + this.onChange(e)} + data-test-id={this.props['data-test-id']}/>} + + {type === 'checkbox' && + this.onChangeCheckBox(e)} + disabled={isReadOnlyMode || Boolean(disabled)} + checked={value} + data-test-id={this.props['data-test-id']}>{label}} + + {type === 'radio' && + this.onChangeRadio(e)} + data-test-id={this.props['data-test-id']}>{label}} + {type === 'select' && + this.optionSelect(e) } + componentClass={type} + name={name} {...inputProps} + data-test-id={this.props['data-test-id']}/>} + + + + ); + } + + 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(e) { + let {onChange} = this.props; + this.setState({ + checked: e.target.checked + }); + onChange(this.state.value); + } + + focus() { + ReactDOM.findDOMNode(this.inputWrapper).focus(); + } + +} +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 new file mode 100644 index 0000000000..95144b1468 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/input/validation/Tabs.jsx @@ -0,0 +1,79 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import {default as BTabs} from 'react-bootstrap/lib/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 { + + static propTypes = { + children: React.PropTypes.node + }; + + cloneTab(element) { + const {invalidTabs} = this.props; + return React.cloneElement( + element, + { + key: element.props.eventKey, + tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab' + } + ); + } + + showTabsError() { + const {invalidTabs} = this.props; + const showError = ((invalidTabs.length === 1 && invalidTabs[0] !== this.props.activeKey) || (invalidTabs.length > 1)); + return showError; + } + + render() { + // eslint-disable-next-line no-unused-vars + let {invalidTabs, ...tabProps} = this.props; + return ( +
    + + {this.props.children.map(element => this.cloneTab(element))} + + { + let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.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(.active):nth-of-type(n)'); + return target && target.offsetParent ? target.offsetParent : this; + }}> + + {i18n('One or more tabs are invalid')} + + +
    + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx index a87c8d6f40..ebb1473c04 100644 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx +++ b/openecomp-ui/src/nfvo-components/input/validation/ValidationButtons.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ /** * Holds the buttons for save/reset for forms. * Used by the ValidationForm that changes the state of the buttons according to its own state. @@ -8,7 +23,7 @@ import React from 'react'; import i18n from 'nfvo-utils/i18n/i18n.js'; import Button from 'react-bootstrap/lib/Button.js'; -import FontAwesome from 'react-fontawesome'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; class ValidationButtons extends React.Component { @@ -22,8 +37,8 @@ class ValidationButtons extends React.Component { }; render() { - var submitBtn = this.props.labledButtons ? i18n('Save') : ; - var closeBtn = this.props.labledButtons ? i18n('Cancel') : ; + var submitBtn = this.props.labledButtons ? i18n('Save') : ; + var closeBtn = this.props.labledButtons ? i18n('Cancel') : ; return (
    {!this.props.isReadOnlyMode ? diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx deleted file mode 100644 index 098ccf1fd4..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationForm.jsx +++ /dev/null @@ -1,200 +0,0 @@ -/** - * ValidationForm should be used in order to have a form that handles it's internal validation state. - * All ValidationInputs inside the form are checked for validity and the styling and submit buttons - * are updated accordingly. - * - * The properties that ahould be given to the form: - * labledButtons - whether or not use icons only as the form default buttons or use buttons with labels - * onSubmit - function for click on the submit button - * onReset - function for click on the reset button - */ -import React from 'react'; -import JSONSchema from 'nfvo-utils/json/JSONSchema.js'; -import JSONPointer from 'nfvo-utils/json/JSONPointer.js'; -import ValidationButtons from './ValidationButtons.jsx'; - -class ValidationForm extends React.Component { - - static childContextTypes = { - validationParent: React.PropTypes.any, - isReadOnlyMode: React.PropTypes.bool, - validationSchema: React.PropTypes.instanceOf(JSONSchema), - validationData: React.PropTypes.object - }; - - static defaultProps = { - hasButtons : true, - onSubmit : null, - onReset : null, - labledButtons: true, - onValidChange : null, - isValid: true - }; - - static propTypes = { - isValid : React.PropTypes.bool, - isReadOnlyMode : React.PropTypes.bool, - hasButtons : React.PropTypes.bool, - onSubmit : React.PropTypes.func, - onReset : React.PropTypes.func, - labledButtons: React.PropTypes.bool, - onValidChange : React.PropTypes.func, - onValidityChanged: React.PropTypes.func, - schema: React.PropTypes.object, - data: React.PropTypes.object - }; - - state = { - isValid: this.props.isValid - }; - - constructor(props) { - super(props); - this.validationComponents = []; - } - - componentWillMount() { - let {schema, data} = this.props; - if (schema) { - this.processSchema(schema, data); - } - } - - componentWillReceiveProps(nextProps) { - let {schema, data} = this.props; - let {schema: nextSchema, data: nextData} = nextProps; - - if (schema !== nextSchema || data !== nextData) { - if (!schema || !nextSchema) { - throw new Error('ValidationForm: dynamically adding/removing schema is not supported'); - } - - if (schema !== nextSchema) { - this.processSchema(nextSchema, nextData); - } else { - this.setState({data: nextData}); - } - } - } - - processSchema(rawSchema, rawData) { - let schema = new JSONSchema(); - schema.setSchema(rawSchema); - let data = schema.processData(rawData); - this.setState({ - schema, - data - }); - } - - render() { - // eslint-disable-next-line no-unused-vars - let {isValid, isReadOnlyMode, hasButtons, onSubmit, labledButtons, onValidChange, onValidityChanged, schema, data, children, ...formProps} = this.props; - return ( -
    this.handleFormSubmit(event)}> -
    {children}
    - {hasButtons && } - - ); - } - - handleFormSubmit(event) { - event.preventDefault(); - let isFormValid = true; - this.validationComponents.forEach(validationComponent => { - const isInputValid = validationComponent.validate().isValid; - isFormValid = isInputValid && isFormValid; - }); - if(isFormValid && this.props.onSubmit) { - return this.props.onSubmit(event); - } else if(!isFormValid) { - this.setState({isValid: false}); - } - } - - componentWillUpdate(nextProps, nextState) { - if(this.state.isValid !== nextState.isValid && this.props.onValidityChanged) { - this.props.onValidityChanged(nextState.isValid); - } - } - - componentDidUpdate(prevProps, prevState) { - // 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.refs.buttons.setState({isValid: this.state.isValid}); - } - } else if(this.state.isValid !== prevState.isValid) { - if (this.props.hasButtons) { - this.refs.buttons.setState({isValid: this.state.isValid}); - } - // callback in case form is part of bigger picture in view - if (this.props.onValidChange) { - this.props.onValidChange(this.state.isValid); - } - } - } - - componentDidMount() { - if (this.props.hasButtons) { - this.refs.buttons.setState({isValid: this.state.isValid}); - } - } - - - getChildContext() { - return { - validationParent: this, - isReadOnlyMode: this.props.isReadOnlyMode, - validationSchema: this.state.schema, - validationData: this.state.data - }; - } - - - /*** - * Used by ValidationInput in order to let the (parent) form know - * the valid state. If there is a change in the state of the form, - * the buttons will be updated. - * - * @param validationComponent - * @param isValid - */ - childValidStateChanged(validationComponent, isValid) { - if (isValid !== this.state.isValid) { - let oldState = this.state.isValid; - let newState = isValid && this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent).every(otherValidationComponent => { - return otherValidationComponent.isValid(); - }); - - if (oldState !== newState) { - this.setState({isValid: newState}); - } - } - } - - register(validationComponent) { - if (this.state.schema) { - // TODO: register - } else { - this.validationComponents.push(validationComponent); - } - } - - unregister(validationComponent) { - this.childValidStateChanged(validationComponent, true); - this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); - } - - onValueChanged(pointer, value, isValid, error) { - this.props.onDataChanged({ - data: JSONPointer.setValue(this.props.data, pointer, value), - isValid, - error - }); - } -} - - -export default ValidationForm; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx deleted file mode 100644 index 0f14307645..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationInput.jsx +++ /dev/null @@ -1,509 +0,0 @@ -/** - * Used for inputs on a validation form. - * All properties will be passed on to the input element. - * - * The following properties can be set for OOB validations and callbacks: - - required: Boolean: Should be set to true if the input must have a value - - numeric: Boolean : Should be set to true id the input should be an integer - - onChange : Function : Will be called to validate the value if the default validations are not sufficient, should return a boolean value - indicating whether the value is valid - - didUpdateCallback :Function: Will be called after the state has been updated and the component has rerendered. This can be used if - there are dependencies between inputs in a form. - * - * The following properties of the state can be set to determine - * the state of the input from outside components: - - isValid : Boolean - whether the value is valid - - value : value for the input field, - - disabled : Boolean, - - required : Boolean - whether the input value must be filled out. - */ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Validator from 'validator'; -import FormGroup from 'react-bootstrap/lib/FormGroup.js'; -import Input from 'react-bootstrap/lib/Input.js'; -import Overlay from 'react-bootstrap/lib/Overlay.js'; -import Tooltip from 'react-bootstrap/lib/Tooltip.js'; -import isEqual from 'lodash/isEqual.js'; -import i18n from 'nfvo-utils/i18n/i18n.js'; -import JSONSchema from 'nfvo-utils/json/JSONSchema.js'; -import JSONPointer from 'nfvo-utils/json/JSONPointer.js'; - - -import InputOptions from '../inputOptions/InputOptions.jsx'; - -const globalValidationFunctions = { - required: value => value !== '', - maxLength: (value, length) => Validator.isLength(value, {max: length}), - minLength: (value, length) => Validator.isLength(value, {min: length}), - pattern: (value, pattern) => Validator.matches(value, pattern), - numeric: value => { - if (value === '') { - // to allow empty value which is not zero - return true; - } - return Validator.isNumeric(value); - }, - maxValue: (value, maxValue) => value < maxValue, - minValue: (value, minValue) => value >= minValue, - alphanumeric: value => Validator.isAlphanumeric(value), - alphanumericWithSpaces: value => Validator.isAlphanumeric(value.replace(/ /g, '')), - validateName: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-/g, ''), 'en-US'), - validateVendorName: value => Validator.isAlphanumeric(value.replace(/[\x7F-\xFF]|\s/g, ''), 'en-US'), - freeEnglishText: value => Validator.isAlphanumeric(value.replace(/\s|\.|\_|\-|\,|\(|\)|\?/g, ''), 'en-US'), - email: value => Validator.isEmail(value), - ip: value => Validator.isIP(value), - url: value => Validator.isURL(value) -}; - -const globalValidationMessagingFunctions = { - required: () => i18n('Field is required'), - maxLength: (value, maxLength) => i18n('Field value has exceeded it\'s limit, {maxLength}. current length: {length}', { - length: value.length, - maxLength - }), - minLength: (value, minLength) => i18n('Field value should contain at least {minLength} characters.', {minLength}), - pattern: (value, pattern) => i18n('Field value should match the pattern: {pattern}.', {pattern}), - numeric: () => i18n('Field value should contain numbers only.'), - maxValue: (value, maxValue) => i18n('Field value should be less than: {maxValue}.', {maxValue}), - minValue: (value, minValue) => i18n('Field value should be at least: {minValue}.', {minValue}), - alphanumeric: () => i18n('Field value should contain letters or digits only.'), - alphanumericWithSpaces: () => i18n('Field value should contain letters, digits or spaces only.'), - validateName: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'), - validateVendorName: ()=> i18n('Field value should contain English letters digits and spaces only.'), - freeEnglishText: ()=> i18n('Field value should contain English letters, digits , spaces, underscores, dashes and dots only.'), - email: () => i18n('Field value should be a valid email address.'), - ip: () => i18n('Field value should be a valid ip address.'), - url: () => i18n('Field value should be a valid url address.'), - general: () => i18n('Field value is invalid.') -}; - -class ValidationInput extends React.Component { - - static contextTypes = { - validationParent: React.PropTypes.any, - isReadOnlyMode: React.PropTypes.bool, - validationSchema: React.PropTypes.instanceOf(JSONSchema), - validationData: React.PropTypes.object - }; - - static defaultProps = { - onChange: null, - disabled: null, - didUpdateCallback: null, - validations: {}, - value: '' - }; - - static propTypes = { - type: React.PropTypes.string.isRequired, - onChange: React.PropTypes.func, - disabled: React.PropTypes.bool, - didUpdateCallback: React.PropTypes.func, - validations: React.PropTypes.object, - isMultiSelect: React.PropTypes.bool, - onOtherChange: React.PropTypes.func, - pointer: React.PropTypes.string - }; - - - state = { - isValid: true, - style: null, - value: this.props.value, - error: {}, - previousErrorMessage: '', - wasInvalid: false, - validations: this.props.validations, - isMultiSelect: this.props.isMultiSelect - }; - - componentWillMount() { - if (this.context.validationSchema) { - let {validationSchema: schema, validationData: data} = this.context, - {pointer} = this.props; - - if (!schema.exists(pointer)) { - console.error(`Field doesn't exists in the schema ${pointer}`); - } - - let value = JSONPointer.getValue(data, pointer); - if (value === undefined) { - value = schema.getDefault(pointer); - if (value === undefined) { - value = ''; - } - } - this.setState({value}); - - let enums = schema.getEnum(pointer); - if (enums) { - let values = enums.map(value => ({enum: value, title: value, groupName: pointer})), - isMultiSelect = schema.isArray(pointer); - - if (!isMultiSelect && this.props.type !== 'radiogroup') { - values = [{enum: '', title: i18n('Select...')}, ...values]; - } - if (isMultiSelect && Array.isArray(value) && value.length === 0) { - value = ''; - } - - this.setState({ - isMultiSelect, - values, - onEnumChange: value => this.changedInputOptions(value), - value - }); - } - - this.setState({validations: this.extractValidationsFromSchema(schema, pointer, this.props)}); - } - } - - extractValidationsFromSchema(schema, pointer, props) { - /* props are here to get precedence over the scheme definitions */ - let validations = {}; - - if (schema.isRequired(pointer)) { - validations.required = true; - } - - if (schema.isNumber(pointer)) { - validations.numeric = true; - - const maxValue = props.validations.maxValue || schema.getMaxValue(pointer); - if (maxValue !== undefined) { - validations.maxValue = maxValue; - } - - const minValue = props.validations.minValue || schema.getMinValue(pointer); - if (minValue !== undefined) { - validations.minValue = minValue; - } - } - - - if (schema.isString(pointer)) { - - const pattern = schema.getPattern(pointer); - if (pattern) { - validations.pattern = pattern; - } - - const maxLength = schema.getMaxLength(pointer); - if (maxLength !== undefined) { - validations.maxLength = maxLength; - } - - const minLength = schema.getMinLength(pointer); - if (minLength !== undefined) { - validations.minLength = minLength; - } - } - - return validations; - } - - componentWillReceiveProps({value: nextValue, validations: nextValidations, pointer: nextPointer}, nextContext) { - const {validations, value} = this.props; - const validationsChanged = !isEqual(validations, nextValidations); - if (nextContext.validationSchema) { - if (this.props.pointer !== nextPointer || - this.context.validationData !== nextContext.validationData) { - let currentValue = JSONPointer.getValue(this.context.validationData, this.props.pointer), - nextValue = JSONPointer.getValue(nextContext.validationData, nextPointer); - if(nextValue === undefined) { - nextValue = ''; - } - if (this.state.isMultiSelect && Array.isArray(nextValue) && nextValue.length === 0) { - nextValue = ''; - } - if (currentValue !== nextValue) { - this.setState({value: nextValue}); - } - if (validationsChanged) { - this.setState({ - validations: this.extractValidationsFromSchema(nextContext.validationSchema, nextPointer, {validations: nextValidations}) - }); - } - } - } else { - if (validationsChanged) { - this.setState({validations: nextValidations}); - } - if (this.state.wasInvalid && (value !== nextValue || validationsChanged)) { - this.validate(nextValue, nextValidations); - } else if (value !== nextValue) { - this.setState({value: nextValue}); - } - } - } - - shouldTypeBeNumberBySchemeDefinition(pointer) { - return this.context.validationSchema && - this.context.validationSchema.isNumber(pointer); - } - - hasEnum(pointer) { - return this.context.validationSchema && - this.context.validationSchema.getEnum(pointer); - } - - render() { - let {value, isMultiSelect, values, onEnumChange, style, isValid, validations} = this.state; - let {onOtherChange, type, pointer} = this.props; - if (this.shouldTypeBeNumberBySchemeDefinition(pointer) && !this.hasEnum(pointer)) { - type = 'number'; - } - let props = {...this.props}; - - let groupClasses = this.props.groupClassName || ''; - if (validations.required) { - groupClasses += ' required'; - } - let isReadOnlyMode = this.context.isReadOnlyMode; - - if (value === true && (type === 'checkbox' || type === 'radio')) { - props.checked = true; - } - return ( -
    - { - !isMultiSelect && !onOtherChange && type !== 'select' && type !== 'radiogroup' - && this.changedInput()} - onBlur={() => this.blurInput()}> - {this.props.children} - - } - { - type === 'radiogroup' - && - { - values.map(val => - this.changedInput()}/> - ) - } - - } - { - (isMultiSelect || onOtherChange || type === 'select') - && this.changedInput()} - onBlur={() => this.blurInput()} - hasError={!isValid} - ref={'_myInput'} - isMultiSelect={isMultiSelect} - values={values} - onEnumChange={onEnumChange} - selectedEnum={value} - multiSelectedEnum={value} - {...props} /> - } - {this.renderOverlay()} -
    - ); - } - - renderOverlay() { - let position = 'right'; - if (this.props.type === 'text' - || this.props.type === 'email' - || this.props.type === 'number' - || this.props.type === 'password' - - ) { - position = 'bottom'; - } - - let validationMessage = this.state.error.message || this.state.previousErrorMessage; - return ( - { - let target = ReactDOM.findDOMNode(this.refs._myInput); - return target.offsetParent ? target : undefined; - }} - container={this}> - - {validationMessage} - - - ); - } - - componentDidMount() { - if (this.context.validationParent) { - this.context.validationParent.register(this); - } - } - - componentDidUpdate(prevProps, prevState) { - if (this.context.validationParent) { - if (prevState.isValid !== this.state.isValid) { - this.context.validationParent.childValidStateChanged(this, this.state.isValid); - } - } - if (this.props.didUpdateCallback) { - this.props.didUpdateCallback(); - } - - } - - componentWillUnmount() { - if (this.context.validationParent) { - this.context.validationParent.unregister(this); - } - } - - isNumberInputElement() { - return this.props.type === 'number' || this.refs._myInput.props.type === 'number'; - } - - /*** - * Adding same method as the actual input component - * @returns {*} - */ - getValue() { - if (this.props.type === 'checkbox') { - return this.refs._myInput.getChecked(); - } - if (this.props.type === 'radiogroup') { - for (let key in this.refs) { // finding the value of the radio button that was checked - if (this.refs[key].getChecked()) { - return this.refs[key].getValue(); - } - } - } - if (this.isNumberInputElement()) { - return Number(this.refs._myInput.getValue()); - } - - return this.refs._myInput.getValue(); - } - - resetValue() { - this.setState({value: this.props.value}); - } - - - /*** - * internal method that validated the value. includes callback to the onChange method - * @param value - * @param validations - map containing validation id and the limitation describing the validation. - * @returns {object} - */ - validateValue = (value, validations) => { - let {customValidationFunction} = validations; - let error = {}; - let isValid = true; - for (let validation in validations) { - if ('customValidationFunction' !== validation) { - if (validations[validation]) { - if (!globalValidationFunctions[validation](value, validations[validation])) { - error.id = validation; - error.message = globalValidationMessagingFunctions[validation](value, validations[validation]); - isValid = false; - break; - } - } - } else { - let customValidationResult = customValidationFunction(value); - - if (customValidationResult !== true) { - error.id = 'custom'; - isValid = false; - if (typeof customValidationResult === 'string') {//custom validation error message supplied. - error.message = customValidationResult; - } else { - error.message = globalValidationMessagingFunctions.general(); - } - break; - } - - - } - } - - return { - isValid, - error - }; - }; - - /*** - * Internal method that handles the change event of the input. validates and updates the state. - */ - changedInput() { - - let {isValid, error} = this.state.wasInvalid ? this.validate() : this.state; - let onChange = this.props.onChange; - if (onChange) { - onChange(this.getValue(), isValid, error); - } - if (this.context.validationSchema) { - let value = this.getValue(); - if (this.state.isMultiSelect && value === '') { - value = []; - } - if (this.shouldTypeBeNumberBySchemeDefinition(this.props.pointer)) { - value = Number(value); - } - this.context.validationParent.onValueChanged(this.props.pointer, value, isValid, error); - } - } - - changedInputOptions(value) { - this.context.validationParent.onValueChanged(this.props.pointer, value, true); - } - - blurInput() { - if (!this.state.wasInvalid) { - this.setState({wasInvalid: true}); - } - - let {isValid, error} = !this.state.wasInvalid ? this.validate() : this.state; - let onBlur = this.props.onBlur; - if (onBlur) { - onBlur(this.getValue(), isValid, error); - } - } - - validate(value = this.getValue(), validations = this.state.validations) { - let validationStatus = this.validateValue(value, validations); - let {isValid, error} = validationStatus; - let _style = isValid ? null : 'error'; - this.setState({ - isValid, - error, - value, - previousErrorMessage: this.state.error.message || '', - style: _style, - wasInvalid: !isValid || this.state.wasInvalid - }); - - return validationStatus; - } - - isValid() { - return this.state.isValid; - } - -} -export default ValidationInput; diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx deleted file mode 100644 index 6036518288..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationTab.jsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from 'react'; -import Tab from 'react-bootstrap/lib/Tab.js'; - -export default -class ValidationTab extends React.Component { - - static propTypes = { - children: React.PropTypes.node, - eventKey: React.PropTypes.any.isRequired, - onValidationStateChange: React.PropTypes.func //This property is assigned dynamically via React.cloneElement. lookup ValidationTabs.jsx. therefore it cannot be stated as required! - }; - - constructor(props) { - super(props); - this.validationComponents = []; - } - - static childContextTypes = { - validationParent: React.PropTypes.any - }; - - static contextTypes = { - validationParent: React.PropTypes.any - }; - - getChildContext() { - return {validationParent: this}; - } - - state = { - isValid: true, - notifyParent: false - }; - - componentDidMount() { - let validationParent = this.context.validationParent; - if (validationParent) { - validationParent.register(this); - } - } - - componentWillUnmount() { - let validationParent = this.context.validationParent; - if (validationParent) { - validationParent.unregister(this); - } - } - - register(validationComponent) { - this.validationComponents.push(validationComponent); - } - - unregister(validationComponent) { - this.childValidStateChanged(validationComponent, true); - this.validationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); - } - - notifyValidStateChangedToParent(isValid) { - - let validationParent = this.context.validationParent; - if (validationParent) { - validationParent.childValidStateChanged(this, isValid); - } - } - - childValidStateChanged(validationComponent, isValid) { - - const currentValidState = this.state.isValid; - if (isValid !== currentValidState) { - let filteredValidationComponents = this.validationComponents.filter(otherValidationComponent => validationComponent !== otherValidationComponent); - let newValidState = isValid && filteredValidationComponents.every(otherValidationComponent => { - return otherValidationComponent.isValid(); - }); - this.setState({isValid: newValidState, notifyParent: true}); - } - } - - validate() { - let isValid = true; - this.validationComponents.forEach(validationComponent => { - const isValidationComponentValid = validationComponent.validate().isValid; - isValid = isValidationComponentValid && isValid; - }); - this.setState({isValid, notifyParent: false}); - return {isValid}; - } - - componentDidUpdate(prevProps, prevState) { - if(prevState.isValid !== this.state.isValid) { - if(this.state.notifyParent) { - this.notifyValidStateChangedToParent(this.state.isValid); - } - this.props.onValidationStateChange(this.props.eventKey, this.state.isValid); - } - } - - isValid() { - return this.state.isValid; - } - - render() { - let {children, ...tabProps} = this.props; - return ( - {children} - ); - } -} diff --git a/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx b/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx deleted file mode 100644 index 6eda4b9827..0000000000 --- a/openecomp-ui/src/nfvo-components/input/validation/ValidationTabs.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import Tabs from 'react-bootstrap/lib/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 ValidationTab extends React.Component { - - static propTypes = { - children: React.PropTypes.node - }; - - state = { - invalidTabs: [] - }; - - cloneTab(element) { - const {invalidTabs} = this.state; - return React.cloneElement( - element, - { - key: element.props.eventKey, - tabClassName: invalidTabs.indexOf(element.props.eventKey) > -1 ? 'invalid-tab' : 'valid-tab', - onValidationStateChange: (eventKey, isValid) => this.validTabStateChanged(eventKey, isValid) - } - ); - } - - validTabStateChanged(eventKey, isValid) { - let {invalidTabs} = this.state; - let invalidTabIndex = invalidTabs.indexOf(eventKey); - if (isValid && invalidTabIndex > -1) { - this.setState({invalidTabs: invalidTabs.filter(otherEventKey => eventKey !== otherEventKey)}); - } else if (!isValid && invalidTabIndex === -1) { - this.setState({invalidTabs: [...invalidTabs, eventKey]}); - } - } - - showTabsError() { - const {invalidTabs} = this.state; - return invalidTabs.length > 0 && (invalidTabs.length > 1 || invalidTabs[0] !== this.props.activeKey); - } - - render() { - return ( -
    - - {this.props.children.map(element => this.cloneTab(element))} - - { - let target = ReactDOM.findDOMNode(this.refs.tabsList).querySelector('ul > li.invalid-tab:not(.active):nth-of-type(n)'); - return target && target.offsetParent ? target : undefined; - } - } - container={this}> - - {i18n('One or more tabs are invalid')} - - -
    - ); - } -} diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx index e8d0fc2536..f6c906b56b 100644 --- a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemView.jsx @@ -1,7 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; +import classnames from 'classnames'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; import store from 'sdc-app/AppStore.js'; -import NotificationConstants from 'nfvo-components/notifications/NotificationConstants.js'; +import {actionTypes as modalActionTypes} from 'nfvo-components/modal/GlobalModalConstants.js'; class ListEditorItem extends React.Component { static propTypes = { @@ -16,14 +33,14 @@ class ListEditorItem extends React.Component { let {onDelete, onSelect, onEdit, children, isReadOnlyMode} = this.props; let isAbilityToDelete = isReadOnlyMode === undefined ? true : !isReadOnlyMode; return ( -
    +
    {children}
    -
    - {onEdit && this.onClickedItem(onEdit)}/>} - {onDelete && isAbilityToDelete && this.onClickedItem(onDelete)}/>} -
    + {(onEdit || onDelete) &&
    + {onEdit && this.onClickedItem(onEdit)}/>} + {onDelete && isAbilityToDelete && this.onClickedItem(onDelete)}/>} +
    }
    ); } @@ -33,8 +50,11 @@ class ListEditorItem extends React.Component { let {isCheckedOut} = this.props; if (isCheckedOut === false) { store.dispatch({ - type: NotificationConstants.NOTIFY_ERROR, - data: {title: 'Error', msg: 'This item is checkedin/submitted, Click Check Out to continue'} + type: modalActionTypes.GLOBAL_MODAL_WARNING, + data: { + title: i18n('Error'), + msg: i18n('This item is checkedin/submitted, Click Check Out to continue') + } }); } else { diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemViewField.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemViewField.jsx new file mode 100644 index 0000000000..839f9a504a --- /dev/null +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorItemViewField.jsx @@ -0,0 +1,24 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import React from 'react'; + +export const ListEditorItemViewField = ({children}) => ( +
    + {children} +
    +); + +export default ListEditorItemViewField; diff --git a/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx index 1ee91f31f6..cc805e9ada 100644 --- a/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx +++ b/openecomp-ui/src/nfvo-components/listEditor/ListEditorView.jsx @@ -1,12 +1,63 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import Input from 'react-bootstrap/lib/Input.js'; +import classnames from 'classnames'; +import ExpandableInput from 'nfvo-components/input/ExpandableInput.jsx'; +const ListEditorHeader = ({onAdd, isReadOnlyMode, title, plusButtonTitle}) => { + return ( +
    + {title &&
    {title}
    } +
    + { onAdd && +
    + {`+ ${plusButtonTitle}`} +
    + } +
    +
    + ); +}; + +const ListEditorScroller = ({children, twoColumns}) => { + return ( +
    +
    + {children} +
    +
    + ); +}; + +const FilterWrapper = ({onFilter, filterValue}) => { + return ( +
    + +
    + ); +}; class ListEditorView extends React.Component { static defaultProps = { - className: '' + className: '', + twoColumns: false }; static propTypes = { @@ -17,45 +68,17 @@ class ListEditorView extends React.Component { onFilter: React.PropTypes.func, className: React.PropTypes.string, isReadOnlyMode: React.PropTypes.bool, - placeholder: React.PropTypes.string + placeholder: React.PropTypes.string, + twoColumns: React.PropTypes.bool }; render() { - let {title, plusButtonTitle, onAdd, children, filterValue, onFilter, className, placeholder, isReadOnlyMode} = this.props; + let {title, plusButtonTitle, onAdd, children, onFilter, className, isReadOnlyMode, twoColumns, filterValue} = this.props; return ( -
    - {title && onAdd &&
    {title}
    } -
    - {title && !onAdd &&
    {title}
    } -
    - { onAdd && -
    - - {plusButtonTitle} -
    - } -
    - - { - onFilter && -
    - onFilter(this.refs.filter.getValue())}/> - -
    - } -
    -
    -
    - {children} -
    -
    +
    + + {onFilter && (children.length || filterValue) && } +
    ); } diff --git a/openecomp-ui/src/nfvo-components/listEditor/listEditor.stories.js b/openecomp-ui/src/nfvo-components/listEditor/listEditor.stories.js new file mode 100644 index 0000000000..276b05e270 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/listEditor/listEditor.stories.js @@ -0,0 +1,60 @@ +import React from 'react'; +import {storiesOf, action} from '@kadira/storybook'; +import ListEditorView from './ListEditorView.jsx'; +import ListEditorItemView from './ListEditorItemView.jsx'; +import ListEditorItemViewField from './ListEditorItemViewField.jsx'; +import {text, number} from '@kadira/storybook-addon-knobs'; +import {withKnobs} from '@kadira/storybook-addon-knobs'; + +function makeChildren({onEdit = false, onDelete = false} = {}) { + return ( + [...Array(number('Items', 2)).keys()].map(index => ( + + +
    {text('field 1', 'Lorum Ipsum')}
    +
    + +
    {text('field 2', 'Lorum Ipsum')}
    +
    +
    ) + ) + ); +} + +const stories = storiesOf('ListEditor', module); +stories.addDecorator(withKnobs); + +stories + .add('regular', () => ( + + {makeChildren()} + + )) + .add('two columns', () => ( + + {makeChildren()} + + )) + .add('with add', () => ( + + {makeChildren()} + + )) + .add('with delete', () => ( + + {makeChildren({onDelete: action('onDelete')})} + + )) + .add('with edit', () => ( + + {makeChildren({onEdit: action('onEdit')})} + + )) + .add('with edit and delete', () => ( + + {makeChildren({onDelete: action('onDelete'), onEdit: action('onEdit')})} + + )); diff --git a/openecomp-ui/src/nfvo-components/loader/Loader.jsx b/openecomp-ui/src/nfvo-components/loader/Loader.jsx index cc1ffdb2b3..675b04c8ea 100644 --- a/openecomp-ui/src/nfvo-components/loader/Loader.jsx +++ b/openecomp-ui/src/nfvo-components/loader/Loader.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import {connect} from 'react-redux'; diff --git a/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js b/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js index e8e4953eb9..7c0c0e2b08 100644 --- a/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js +++ b/openecomp-ui/src/nfvo-components/loader/LoaderConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionTypes = keyMirror({ diff --git a/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js b/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js index 582eff330d..2eff70a617 100644 --- a/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js +++ b/openecomp-ui/src/nfvo-components/loader/LoaderReducer.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import {actionTypes} from './LoaderConstants.js'; export default (state = {}, action) => { diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModal.js b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js new file mode 100644 index 0000000000..65a1ad683b --- /dev/null +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModal.js @@ -0,0 +1,120 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import React from 'react'; +import {connect} from 'react-redux'; + +import Modal from 'nfvo-components/modal/Modal.jsx'; +import Button from 'react-bootstrap/lib/Button.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {modalContentComponents} from 'sdc-app/common/modal/ModalContentMapper.js'; +import {actionTypes, typeEnum} from './GlobalModalConstants.js'; + + +const typeClass = { + 'default': 'primary', + error: 'danger', + warning: 'warning', + success: 'success' +}; + + +const ModalFooter = ({type, onConfirmed, onDeclined, onClose, confirmationButtonText, cancelButtonText}) => + + + {onConfirmed && } + ; + +ModalFooter.defaultProps = { + type: 'default', + confirmationButtonText: i18n('OK'), + cancelButtonText: i18n('Cancel') +}; + +export const mapStateToProps = ({modal}) => { + const show = !!modal; + return { + show, + ...modal + }; +}; + +export const mapActionToProps = (dispatch) => { + return { + onClose: () => dispatch({type: actionTypes.GLOBAL_MODAL_CLOSE}) + }; +}; + + +export class GlobalModalView extends React.Component { + + static propTypes = { + show: React.PropTypes.bool, + type: React.PropTypes.oneOf(['default', 'error', 'warning', 'success']), + title: React.PropTypes.string, + modalComponentProps: React.PropTypes.object, + modalComponentName: React.PropTypes.string, + onConfirmed: React.PropTypes.func, + onDeclined: React.PropTypes.func, + confirmationButtonText: React.PropTypes.string, + cancelButtonText: React.PropTypes.string + }; + + static defaultProps = { + show: false, + type: 'default', + title: '' + }; + + render() { + let {title, type, show, modalComponentName, modalComponentProps, + modalClassName, msg, onConfirmed, onDeclined, confirmationButtonText, cancelButtonText, onClose} = this.props; + const ComponentToRender = modalContentComponents[modalComponentName]; + return ( + + + {title} + + + {ComponentToRender ? : msg} + + {(onConfirmed || onDeclined || type !== typeEnum.DEFAULT) && + } + + ); + } + + componentDidUpdate() { + if (this.props.timeout) { + setTimeout(this.props.onClose, this.props.timeout); + } + } +}; + +export default connect(mapStateToProps, mapActionToProps, null, {withRef: true})(GlobalModalView); diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js b/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js new file mode 100644 index 0000000000..0a0ed1fd71 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModalConstants.js @@ -0,0 +1,33 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; + +export const actionTypes = keyMirror({ + GLOBAL_MODAL_SHOW: null, + GLOBAL_MODAL_CLOSE: null, + GLOBAL_MODAL_ERROR: null, + GLOBAL_MODAL_WARNING: null, + GLOBAL_MODAL_SUCCESS: null, + +}); + + +export const typeEnum = { + DEFAULT: 'default', + ERROR: 'error', + WARNING: 'warning', + SUCCESS: 'success' +}; diff --git a/openecomp-ui/src/nfvo-components/modal/GlobalModalReducer.js b/openecomp-ui/src/nfvo-components/modal/GlobalModalReducer.js new file mode 100644 index 0000000000..28674ea569 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/modal/GlobalModalReducer.js @@ -0,0 +1,50 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +import {actionTypes, typeEnum} from './GlobalModalConstants.js'; + +export default (state = null, action) => { + switch (action.type) { + case actionTypes.GLOBAL_MODAL_SHOW: + return { + ...action.data + }; + case actionTypes.GLOBAL_MODAL_ERROR: + return { + type: typeEnum.ERROR, + modalClassName: 'notification-modal', + ...action.data + }; + case actionTypes.GLOBAL_MODAL_WARNING: + return { + type: typeEnum.WARNING, + modalClassName: 'notification-modal', + ...action.data + }; + + case actionTypes.GLOBAL_MODAL_SUCCESS: + return { + type: typeEnum.SUCCESS, + modalClassName: 'notification-modal', + ...action.data + }; + + case actionTypes.GLOBAL_MODAL_CLOSE: + return null; + default: + return state; + } +}; diff --git a/openecomp-ui/src/nfvo-components/modal/Modal.jsx b/openecomp-ui/src/nfvo-components/modal/Modal.jsx index be4963ef65..b0f704dba9 100644 --- a/openecomp-ui/src/nfvo-components/modal/Modal.jsx +++ b/openecomp-ui/src/nfvo-components/modal/Modal.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import ReactDOM from 'react-dom'; import BootstrapModal from 'react-bootstrap/lib/Modal.js'; diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js b/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js deleted file mode 100644 index 1a53f4c135..0000000000 --- a/openecomp-ui/src/nfvo-components/notifications/NotificationConstants.js +++ /dev/null @@ -1,29 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import keyMirror from 'nfvo-utils/KeyMirror.js'; - -export default keyMirror({ - NOTIFY_ERROR: null, - NOTIFY_SUCCESS: null, - NOTIFY_WARNING: null, - NOTIFY_INFO: null, - NOTIFY_CLOSE: null -}); diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx b/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx deleted file mode 100644 index 71793097fb..0000000000 --- a/openecomp-ui/src/nfvo-components/notifications/NotificationModal.jsx +++ /dev/null @@ -1,100 +0,0 @@ -/** - * NotificationModal options: - * - * show: whether to show notification or not, - * type: the type of the notification. valid values are: 'default', 'error', 'warning', 'success' - * msg: the notification content. could be a string or node (React component) - * title: the notification title - * timeout: timeout for the notification to fade out. if timeout == 0 then the notification is rendered until the user closes it - * - */ -import React, {Component, PropTypes} from 'react'; -import {connect} from 'react-redux'; -import Button from 'react-bootstrap/lib/Button.js'; - -import i18n from 'nfvo-utils/i18n/i18n.js'; -import Modal from 'nfvo-components/modal/Modal.jsx'; -import SubmitErrorResponse from 'nfvo-components/SubmitErrorResponse.jsx'; -import NotificationConstants from './NotificationConstants.js'; - -let typeClass = { - 'default': 'primary', - error: 'danger', - warning: 'warning', - success: 'success' -}; - -const mapActionsToProps = (dispatch) => { - return {onCloseClick: () => dispatch({type: NotificationConstants.NOTIFY_CLOSE})}; -}; - -const mapStateToProps = ({notification}) => { - - let show = notification !== null && notification.title !== 'Conflict'; - let mapResult = {show}; - if (show) { - mapResult = {show, ...notification}; - } - - return mapResult; -}; - -export class NotificationModal extends Component { - - static propTypes = { - show: PropTypes.bool, - type: PropTypes.oneOf(['default', 'error', 'warning', 'success']), - title: PropTypes.string, - msg: PropTypes.node, - validationResponse: PropTypes.object, - timeout: PropTypes.number - }; - - static defaultProps = { - show: false, - type: 'default', - title: '', - msg: '', - timeout: 0 - }; - - state = {type: undefined}; - - componentWillReceiveProps(nextProps) { - if (this.props.show !== nextProps.show && nextProps.show === false) { - this.setState({type: this.props.type}); - } - else { - this.setState({type: undefined}); - } - } - - componentDidUpdate() { - if (this.props.timeout) { - setTimeout(this.props.onCloseClick, this.props.timeout); - } - } - - render() { - let {title, type, msg, show, validationResponse, onCloseClick} = this.props; - if (!show) { - type = this.state.type; - } - if (validationResponse) { - msg = (); - } - return ( - - - {title} - - {msg} - - - - - ); - } -} - -export default connect(mapStateToProps, mapActionsToProps)(NotificationModal); diff --git a/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js b/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js deleted file mode 100644 index c8b30d6e50..0000000000 --- a/openecomp-ui/src/nfvo-components/notifications/NotificationReducer.js +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -import NotificationConstants from './NotificationConstants.js'; - -export default (state = null, action) => { - switch (action.type) { - case NotificationConstants.NOTIFY_INFO: - return {type: 'default', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout}; - - case NotificationConstants.NOTIFY_ERROR: - return { - type: 'error', - title: action.data.title, - msg: action.data.msg, - validationResponse: action.data.validationResponse, - timeout: action.data.timeout - }; - - case NotificationConstants.NOTIFY_WARNING: - return {type: 'warning', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout}; - - case NotificationConstants.NOTIFY_SUCCESS: - return { - type: 'success', title: action.data.title, msg: action.data.msg, timeout: action.data.timeout - }; - case NotificationConstants.NOTIFY_CLOSE: - return null; - - default: - return state; - } - -}; diff --git a/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx index feb0f813ea..3b89137090 100644 --- a/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx +++ b/openecomp-ui/src/nfvo-components/panel/NavigationSideBar.jsx @@ -1,9 +1,23 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; import classnames from 'classnames'; import Collapse from 'react-bootstrap/lib/Collapse.js'; class NavigationSideBar extends React.Component { - static PropTypes = { activeItemId: React.PropTypes.string.isRequired, onSelect: React.PropTypes.func, @@ -11,51 +25,26 @@ class NavigationSideBar extends React.Component { groups: React.PropTypes.array }; + constructor(props) { + super(props); + this.state = { + activeItemId: null + }; + this.handleItemClicked = this.handleItemClicked.bind(this); + } + render() { let {groups, activeItemId} = this.props; return (
    {groups.map(group => ( -
    -
    {group.name}
    -
    - { - group.items && group.items.map(item => this.renderGroupItem(item, activeItemId)) - } -
    -
    + ))}
    ); } - renderGroupItem(item, activeItemId) { - let isGroup = item.items && item.items.length > 0; - return ( -
    -
    this.handleItemClicked(event, item)}> - {item.name} -
    - {isGroup && - -
    - {item.items.map(item => this.renderGroupItem(item, activeItemId))} -
    -
    - } -
    - ); - } - handleItemClicked(event, item) { event.stopPropagation(); if(this.props.onToggle) { @@ -70,4 +59,70 @@ class NavigationSideBar extends React.Component { } } +class NavigationMenu extends React.Component { + static PropTypes = { + activeItemId: React.PropTypes.string.isRequired, + onNavigationItemClick: React.PropTypes.func, + menu: React.PropTypes.array + }; + + render() { + const {menu, activeItemId, onNavigationItemClick} = this.props; + return ( +
    + + +
    ); + } +} + +function NavigationMenuHeader(props) { + return
    {props.title}
    ; +} + +function NavigationMenuItems(props) { + const {items, activeItemId, onNavigationItemClick} = props; + return ( +
    + { + items && items.map(item => ()) + } +
    + ); +} + +function NavigationMenuItem(props) { + const {onNavigationItemClick, item, activeItemId} = props; + const isGroup = item.items && item.items.length > 0; + return ( +
    + + {isGroup && +
    + {item.items.map(subItem => ()) } +
    +
    + } +
    + ); +} + +function NavigationLink(props) { + const {item, activeItemId, onClick} = props; + return ( +
    onClick(event, item)} + data-test-id={'navbar-group-item-' + item.id}> + {item.name} +
    + ); +} + export default NavigationSideBar; diff --git a/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx b/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx deleted file mode 100644 index 10c5326300..0000000000 --- a/openecomp-ui/src/nfvo-components/panel/SlidePanel.jsx +++ /dev/null @@ -1,109 +0,0 @@ -import React from 'react'; -import FontAwesome from 'react-fontawesome'; -import ReactDOM from 'react-dom'; - -class SlidePanel extends React.Component { - - static PropTypes = { - direction: React.PropTypes.string.isRequired, - className: React.PropTypes.string, - title: React.PropTypes.string, - isOpen: React.PropTypes.bool - }; - - static defaultProps = { - title: '', - className: '', - isOpen: true - }; - - state = { - isOpen: this.props.isOpen, - direction: this.props.direction, - width: 0, - arrowWidth: 0 - }; - - componentDidMount() { - this.setSliderPosition(); - } - - componentDidUpdate() { - this.setSliderPosition(); - } - - render() { - - let {children, className} = this.props; - let {isOpen} = this.state; - - return ( -
    - {this.renderHeader(isOpen)} -
    {children}
    -
    - ); - } - - renderHeader(isOpen) { - let {direction: initialDirection, title} = this.props; - let {direction: currentDirection} = this.state; - - let iconName = currentDirection === 'right' ? 'angle-double-right collapse-double-icon' : 'angle-double-left collapse-double-icon'; - - let awestyle = {padding: '5px'}; - - if (!isOpen && initialDirection === 'right') { - awestyle.marginLeft = '-1px'; - } - return ( -
    - { initialDirection === 'left' && {title}} - - { initialDirection === 'right' && {title}} -
    - ); - } - - handleClick = () => { - this.setState({ - isOpen: !this.state.isOpen, - direction: this.state.direction === 'left' ? 'right' : 'left' - }); - } - - setSliderPosition = () => { - - let el = ReactDOM.findDOMNode(this); - let {style} = el; - - let {direction: initialDirection} = this.props; - let arrowIconSize = Math.floor(ReactDOM.findDOMNode(this.refs.arrowIcon).getBoundingClientRect().width) * 2; - if (!this.state.isOpen) { - if (this.props.direction === 'left') { - style.left = arrowIconSize - el.getBoundingClientRect().width + 'px'; - } - if (initialDirection === 'right') { - style.right = arrowIconSize - el.getBoundingClientRect().width + 'px'; - } - } - else { - if (initialDirection === 'left') { - style.left = '0px'; - } - - if (this.props.direction === 'right') { - style.right = '0px'; - } - } - } - -} - -export default SlidePanel; \ No newline at end of file diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx index 78525f84c6..6d900dd0bb 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionController.jsx @@ -1,17 +1,31 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; -import classnames from 'classnames'; import i18n from 'nfvo-utils/i18n/i18n.js'; -import Navbar from 'react-bootstrap/lib/Navbar.js'; -import Nav from 'react-bootstrap/lib/Nav.js'; -import ValidationInput from 'nfvo-components/input/validation/ValidationInput.jsx'; -import {actionsEnum, statusEnum} from './VersionControllerConstants.js'; +import {actionsEnum, statusEnum, statusBarTextMap } from './VersionControllerConstants.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; class VersionController extends React.Component { static propTypes = { - version: React.PropTypes.string, + version: React.PropTypes.object, viewableVersions: React.PropTypes.array, onVersionSwitching: React.PropTypes.func, isCheckedOut: React.PropTypes.bool.isRequired, @@ -23,143 +37,146 @@ class VersionController extends React.Component { }; render() { - let {status, isCheckedOut, version = '', viewableVersions = [], onVersionSwitching, callVCAction, onSave, isFormDataValid, onClose} = this.props; + let {status, isCheckedOut, version = {}, viewableVersions = [], onVersionSwitching, callVCAction, onSave, isFormDataValid, onClose} = this.props; let isCheckedIn = Boolean(status === statusEnum.CHECK_IN_STATUS); - let isLatestVersion = Boolean(version === viewableVersions[viewableVersions.length - 1]); + let isLatestVersion = Boolean(version.id === viewableVersions[viewableVersions.length - 1].id); if (!isLatestVersion) { status = statusEnum.PREVIOUS_VERSION; } - return (
    - - - - - - +
    +
    + + +
    +
    + this.submit(callVCAction, version) : undefined} + onRevert={callVCAction ? () => this.revertCheckout(callVCAction, version) : undefined} + status={status} + onCheckinCheckout={callVCAction ? () => this.checkinCheckoutVersion(callVCAction, version) : undefined} + onSave={onSave ? () => onSave() : undefined} + isLatestVersion={isLatestVersion} + isCheckedOut={isCheckedOut} + isCheckedIn={isCheckedIn} isFormDataValid={isFormDataValid} version={version}/> + {onClose &&
    onClose()} data-test-id='vc-cancel-btn'> X
    } +
    +
    ); } - renderStatus(status) { - switch (status) { - case statusEnum.CHECK_OUT_STATUS: - return ( -
    -
    -
    {i18n('CHECKED OUT')}
    -
    - ); - case statusEnum.LOCK_STATUS: - return ( -
    {i18n('LOCKED')}
    - ); - case statusEnum.CHECK_IN_STATUS: - return ( -
    {i18n('CHECKED IN')}
    - ); - case statusEnum.SUBMIT_STATUS: - return ( -
    {i18n('SUBMITTED')}
    - ); - default: - return ( -
    {i18n(status)}
    - ); - } + submit(callVCAction, version) { + const action = actionsEnum.SUBMIT; + callVCAction(action, version); + } + + revertCheckout(callVCAction, version) { + const action = actionsEnum.UNDO_CHECK_OUT; + callVCAction(action, version); } - checkinCheckoutVersion(callVCAction) { + checkinCheckoutVersion(callVCAction, version) { if (this.props.isCheckedOut) { - this.checkin(callVCAction); + this.checkin(callVCAction, version); } else { - this.checkout(callVCAction); + this.checkout(callVCAction, version); } } - - checkin(callVCAction) { - + checkin(callVCAction, version) { const action = actionsEnum.CHECK_IN; - if (this.props.onSave) { this.props.onSave().then(()=>{ - callVCAction(action); - }); + callVCAction(action, version); + }); }else{ - callVCAction(action); + callVCAction(action, version); } } - - checkout(callVCAction) { + checkout(callVCAction, version) { const action = actionsEnum.CHECK_OUT; - callVCAction(action); + callVCAction(action, version); } +} - submit(callVCAction) { - const action = actionsEnum.SUBMIT; - callVCAction(action); +class ActionButtons extends React.Component { + static propTypes = { + version: React.PropTypes.object, + onSubmit: React.PropTypes.func, + onRevert: React.PropTypes.func, + onSave: React.PropTypes.func, + isLatestVersion: React.PropTypes.bool, + isCheckedIn: React.PropTypes.bool, + isCheckedOut: React.PropTypes.bool, + isFormDataValid: React.PropTypes.bool + }; + render() { + const {onSubmit, onRevert, onSave, isLatestVersion, isCheckedIn, isCheckedOut, isFormDataValid, version, status, onCheckinCheckout} = this.props; + const [checkinBtnIconSvg, checkinCheckoutBtnTitle] = status === statusEnum.CHECK_OUT_STATUS ? + ['version-controller-lock-open', i18n('Check In')] : + ['version-controller-lock-closed', i18n('Check Out')]; + const disabled = (isLatestVersion && onCheckinCheckout && status !== statusEnum.LOCK_STATUS) ? false : true; + return ( +
    + + {onSubmit && onRevert && +
    + + +
    + } + {onSave && + onSave()} isDisabled={!isCheckedOut || !isFormDataValid || !isLatestVersion} + name='version-controller-save' tooltipText={i18n('Save')}/> + } +
    + ); } +} - revertCheckout(callVCAction) { - const action = actionsEnum.UNDO_CHECK_OUT; - callVCAction(action); - } +function StatusBarUpdates({status}) { + return ( +
    + {i18n(statusBarTextMap[status])} +
    + ); +} + +function VCButton({name, tooltipText, isDisabled, onClick, dataTestId}) { + let onClickAction = isDisabled ? ()=>{} : onClick; + let disabled = isDisabled ? 'disabled' : ''; + + return ( + {tooltipText}}> +
    + +
    +
    + ); +} + +function VersionSelector(props) { + let {version = {}, viewableVersions = [], onVersionSwitching} = props; + const includedVersions = viewableVersions.filter(ver => {return ver.id === version.id;}); + return (
    + +
    ); } export default VersionController; diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js index 9251fd12c4..9af142433c 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerConstants.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import keyMirror from 'nfvo-utils/KeyMirror.js'; export const actionsEnum = keyMirror({ @@ -36,3 +31,11 @@ export const statusEnum = keyMirror({ PREVIOUS_VERSION: 'READ ONLY' }); +export const statusBarTextMap = keyMirror({ + 'Locked': 'Checked Out', + 'LockedByUser': '', + 'Available': 'Checked In', + 'Final': 'Submitted', + 'READ ONLY': 'Locked' +}); + diff --git a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js index de9914454c..e8c12abec3 100644 --- a/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js +++ b/openecomp-ui/src/nfvo-components/panel/versionController/VersionControllerUtils.js @@ -1,23 +1,18 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ +/*! * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. */ - import Configuration from 'sdc-app/config/Configuration.js'; import {statusEnum} from './VersionControllerConstants.js'; @@ -25,24 +20,32 @@ import {statusEnum} from './VersionControllerConstants.js'; const VersionControllerUtils = { getCheckOutStatusKindByUserID(status, lockingUser) { - let currentLoginUserID = Configuration.get('ATTUserID'); - let isCheckedOut = currentLoginUserID === lockingUser; + let returnStatus; + let isCheckedOut; + let currentLoginUserID = Configuration.get('UserID'); + if (lockingUser) { + isCheckedOut = currentLoginUserID === lockingUser; + returnStatus = isCheckedOut ? status : statusEnum.LOCK_STATUS; + } else { + isCheckedOut = false; + returnStatus = status; + } return { - status: isCheckedOut ? status : statusEnum.LOCK_STATUS, + status: returnStatus, isCheckedOut }; }, isCheckedOutByCurrentUser(resource) { - let currentLoginUserID = Configuration.get('ATTUserID'); + let currentLoginUserID = Configuration.get('UserID'); return resource.lockingUser !== undefined && resource.lockingUser === currentLoginUserID; }, isReadOnly(resource) { const {version, viewableVersions = []} = resource; const latestVersion = viewableVersions[viewableVersions.length - 1]; - return version !== latestVersion || !VersionControllerUtils.isCheckedOutByCurrentUser(resource); + return version.id !== latestVersion.id || !VersionControllerUtils.isCheckedOutByCurrentUser(resource); } }; diff --git a/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx b/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx index d786aeef8b..40720c39f4 100644 --- a/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx +++ b/openecomp-ui/src/nfvo-components/progressBar/ProgressBar.jsx @@ -1,3 +1,18 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ import React from 'react'; class ProgressBar extends React.Component { diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx new file mode 100644 index 0000000000..06cb98bbe8 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTable.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import uuid from 'uuid-js'; + +export default class SelectActionTable extends React.Component { + + render() { + let {columns, onAdd, isReadOnlyMode, children, onAddItem} = this.props; + return ( +
    +
    + {onAdd && onAddItem &&
    {onAddItem}
    } + +
    +
    +
    + {columns.map(column =>
    {i18n(column)}
    )} + + +
    +
    + {children} +
    +
    +
    + ); + } +} diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTableCell.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTableCell.jsx new file mode 100644 index 0000000000..2664c8e944 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTableCell.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import SelectInput from 'nfvo-components/input/SelectInput.jsx'; + +const SelectActionTableCell = ({options, selected, disabled, onChange, clearable = true, placeholder}) => { + return ( +
    + onChange(option ? option.value : null)} + clearable={clearable} + options={options} /> +
    + ); +}; + +export default SelectActionTableCell; diff --git a/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx new file mode 100644 index 0000000000..17d8a17c09 --- /dev/null +++ b/openecomp-ui/src/nfvo-components/table/SelectActionTableRow.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import SVGIcon from '../icon/SVGIcon.jsx'; +import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger.js'; +import Tooltip from 'react-bootstrap/lib/Tooltip.js'; + +function tooltip (msg) { + return ( + {msg} + ); +}; + +const IconWithOverlay = ({overlayMsg}) => ( + + + +); + +const SelectActionTableRow = ({children, onDelete, hasError, overlayMsg}) => ( +
    +
    + {children} +
    + {onDelete ? : } + {hasError ? overlayMsg ? : + : hasError === undefined ? : } + +
    +); + +export default SelectActionTableRow; -- cgit 1.2.3-korg