summaryrefslogtreecommitdiffstats
path: root/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx')
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx274
1 files changed, 274 insertions, 0 deletions
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx
new file mode 100644
index 0000000000..25ad90f351
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx
@@ -0,0 +1,274 @@
+/*!
+ * 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';
+import classNames from 'classnames';
+import Collapse from 'react-bootstrap/lib/Collapse.js';
+import Icon from 'nfvo-components/icon/Icon.jsx';
+import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import {mouseActions, errorLevels, nodeFilters} from './HeatValidationConstants.js';
+
+const leftPanelWidth = 250;
+const typeToIcon = Object.freeze({
+ heat: 'heat',
+ volume: 'volume',
+ network: 'network',
+ artifact: 'validation-artifacts',
+ env: 'env',
+ other: 'validation-other'
+});
+
+
+class HeatValidationView extends Component {
+
+ static propTypes = {
+ attachmentsTree: PropTypes.object.isRequired,
+ errorList: PropTypes.array.isRequired,
+ currentErrors: PropTypes.array.isRequired,
+ currentWarnings: PropTypes.array.isRequired,
+ onSelectNode: PropTypes.func.isRequired,
+ onDeselectNode: PropTypes.func.isRequired,
+ toggleExpanded: PropTypes.func.isRequired,
+ selectedNode: PropTypes.string
+ };
+
+ render() {
+ return (<div className='vsp-attachments-heat-validation' data-test-id='heat-validation-editor'>
+ <HeatFileTree errorList={this.props.errorList} attachmentsTree={this.props.attachmentsTree}
+ onSelectNode={this.props.onSelectNode} toggleExpanded={this.props.toggleExpanded}
+ selectedNode={this.props.selectedNode} onDeselectNode={this.props.onDeselectNode} />
+ <HeatMessageBoard errors={this.props.currentErrors} warnings={this.props.currentWarnings} selectedNode={this.props.selectedNode} />
+ </div> );
+ }
+}
+
+function HeatFileTreeRow(props) {
+ let {node, path, toggleExpanded, selectedNode, selectNode} = props;
+ let isFolder = node.children && node.children.length > 0;
+ return (
+ <div onDoubleClick={() => toggleExpanded(path)} className={classNames({
+ 'tree-node-row': true,
+ 'tree-node-clicked': node.name === props.selectedNode
+ })} data-test-id='validation-tree-node'>
+ <div className='name-section'>
+ {
+ isFolder &&
+ <div onClick={() => toggleExpanded(path)}
+ className='tree-node-expander'>
+ <SVGIcon name={!node.expanded ? 'chevron-up' : 'chevron-down'} data-test-id='validation-tree-block-toggle'/>
+ </div>
+ }
+ {
+
+ <span className='tree-node-icon'>
+ <Icon image={typeToIcon[node.type]} iconClassName={selectedNode === node.name ? 'selected' : ''}/>
+ </span>
+ }
+ {
+
+ <span className='tree-node-name' onClick={() => selectNode(node.name)} data-test-id='validation-tree-node-name'>
+ {node.name ? node.name : 'UNKNOWN'}
+ </span>
+ }
+ </div>
+ <ErrorsAndWarningsCount errorList={node.errors} onClick={() => selectNode(node.name)} />
+ </div>);
+}
+
+function HeatFileTreeHeader(props) {
+ let hasErrors = props.errorList.filter(error => error.level === errorLevels.ERROR).length > 0;
+ return (
+ <div onClick={() => props.selectNode(nodeFilters.ALL)} className={classNames({'attachments-tree-header': true,
+ 'header-selected' : props.selectedNode === nodeFilters.ALL})} data-test-id='validation-tree-header'>
+ <div className='tree-header-title' >
+ <Icon image='zip' iconClassName={classNames(props.selectedNode === nodeFilters.ALL ? 'selected' : '', 'header-icon')} />
+ <span className={classNames({'tree-header-title-text' : true,
+ 'tree-header-title-selected' : props.selectedNode === nodeFilters.ALL})}>{i18n(`HEAT${hasErrors ? ' (Draft)' : ''}`)}</span>
+ </div>
+ <ErrorsAndWarningsCount errorList={props.errorList} size='large' />
+ </div>);
+}
+
+class HeatFileTree extends React.Component {
+ static propTypes = {
+ attachmentsTree: PropTypes.object.isRequired,
+ errorList: PropTypes.array.isRequired,
+ onSelectNode: PropTypes.func.isRequired,
+ onDeselectNode: PropTypes.func.isRequired,
+ toggleExpanded: PropTypes.func.isRequired,
+ selectedNode: PropTypes.string
+ };
+ state = {
+ treeWidth: '400'
+ };
+ render() {
+ let {attachmentsTree} = this.props;
+ return (
+ <div className='validation-tree-section' style={{'width' : this.state.treeWidth + 'px'}}>
+ <div className='vsp-attachments-heat-validation-tree'>
+ <div className='tree-wrapper'>
+ {attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind]))}
+ </div>
+ </div>
+ <div onMouseDown={(e) => this.onChangeTreeWidth(e)}
+ className='vsp-attachments-heat-validation-separator' data-test-id='validation-tree-separator'></div>
+ </div>);
+ }
+ renderNode(node, path) {
+ let rand = Math.random() * (3000 - 1) + 1;
+ let isFolder = node.children && node.children.length > 0;
+ let {selectedNode} = this.props;
+ return (
+ <div key={node.name + rand} className={classNames({'tree-block-inside' : !node.header})}>
+ {
+ node.header ?
+ <HeatFileTreeHeader selectedNode={selectedNode} errorList={this.props.errorList} selectNode={(nodeName) => this.selectNode(nodeName)} /> :
+ <HeatFileTreeRow toggleExpanded={this.props.toggleExpanded} node={node} path={path} selectedNode={selectedNode} selectNode={() => this.selectNode(node.name)} />
+ }
+ {
+ isFolder &&
+ <Collapse in={node.expanded}>
+ <div className='tree-node-children'>
+ {
+ node.children.map((child, ind) => this.renderNode(child, [...path, ind]))
+ }
+ </div>
+ </Collapse>
+ }
+ </div>
+ );
+ }
+
+
+
+
+
+ selectNode(currentSelectedNode) {
+ let {onDeselectNode, onSelectNode, selectedNode} = this.props;
+ if (currentSelectedNode !== selectedNode) {
+ onSelectNode(currentSelectedNode);
+ } else {
+ onDeselectNode();
+ }
+
+
+
+ }
+
+ onChangeTreeWidth(e) {
+ if (e.button === mouseActions.MOUSE_BUTTON_CLICK) {
+ let onMouseMove = (e) => {
+ this.setState({treeWidth: e.clientX - leftPanelWidth});
+ };
+ let onMouseUp = () => {
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ };
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+ }
+ }
+}
+
+class HeatMessageBoard extends Component {
+ static propTypes = {
+ currentErrors: PropTypes.array,
+ currentWarnings: PropTypes.array,
+ selectedNode: PropTypes.string
+ };
+ render() {
+ let {errors, warnings} = this.props;
+ let allItems = [...errors, ...warnings];
+ return (
+ <div className='message-board-section'>
+ { allItems.map(error => this.renderError(error)) }
+ </div>
+ );
+ }
+ renderError(error) {
+ let rand = Math.random() * (3000 - 1) + 1;
+ return (
+ <div
+ key={error.name + error.errorMessage + error.parentName + rand}
+ className='error-item' data-test-id='validation-error'>
+ {error.level === errorLevels.WARNING ?
+ <SVGIcon name='exclamation-triangle-line' iconClassName='large' /> : <Icon image='error-lg' /> }
+ <span className='error-item-file-type'>
+ {
+ (this.props.selectedNode === nodeFilters.ALL) ?
+ <span>
+ <span className='error-file-name'>
+ {i18n('{errorName}:', {
+ errorName: error.name
+ })}
+ </span>
+ <span>
+ {i18n('{message}', {message: error.errorMessage})}
+ </span>
+ </span> :
+ i18n('{errorMsg}', {
+ errorMsg: error.errorMessage
+ })
+ }
+ </span>
+ </div>
+ );
+ }
+}
+class ErrorsAndWarningsCount extends Component {
+ static propTypes = {
+ errorList: PropTypes.array,
+ size: PropTypes.string
+ };
+ render() {
+ let errors = this.getErrorsAndWarningsCount(this.props.errorList);
+ if (!errors) {
+ return null;
+ }
+ let errIcon = 'error';
+ let {size} = this.props;
+ if (size && size === 'large') {
+ errIcon += '-lg';
+ }
+ return (<div className='counters'>
+ {(errors.errorCount > 0) && <div className='counter'>
+ <Icon image={errIcon} iconClassName='counter-icon'/>
+ <div className={'error-text ' + (size ? size : '')} data-test-id='validation-error-count'>{errors.errorCount}</div>
+ </div>}
+ {(errors.warningCount > 0) && <div className='counter'>
+ <SVGIcon name='exclamation-triangle-line' iconClassName={size} />
+ <div className={'warning-text ' + (size ? size : '')} data-test-id='validation-warning-count'>{errors.warningCount}</div>
+ </div>}
+ </div>);
+ }
+ getErrorsAndWarningsCount(errorList) {
+ let errorCount = 0, warningCount = 0;
+ if (errorList && errorList.length > 0) {
+ for (let i = 0; i < errorList.length; i++) {
+ if (errorList[i].level === errorLevels.ERROR) {
+ errorCount++;
+ } else if (errorList[i].level === errorLevels.WARNING) {
+ warningCount++;
+ }
+ }
+ }
+ if (errorCount === 0 && warningCount === 0) {
+ return null;
+ }
+ return {errorCount, warningCount};
+ }
+}
+export default HeatValidationView;