summaryrefslogtreecommitdiffstats
path: root/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation
diff options
context:
space:
mode:
authorAviZi <avi.ziv@amdocs.com>2017-06-09 02:39:56 +0300
committerAviZi <avi.ziv@amdocs.com>2017-06-09 02:39:56 +0300
commit280f8015d06af1f41a3ef12e8300801c7a5e0d54 (patch)
tree9c1d3978c04cd28068f02073038c936bb49ca9e0 /openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation
parentfd3821dad11780d33c5373d74c957c442489945e (diff)
[SDC-29] Amdocs OnBoard 1707 initial commit.
Change-Id: Ie4d12a3f574008b792899b368a0902a8b46b5370 Signed-off-by: AviZi <avi.ziv@amdocs.com>
Diffstat (limited to 'openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation')
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js51
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js39
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js57
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js196
-rw-r--r--openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx274
5 files changed, 617 insertions, 0 deletions
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js
new file mode 100644
index 0000000000..21f6e6c77f
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js
@@ -0,0 +1,51 @@
+/*!
+ * 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 HeatValidationView from './HeatValidationView.jsx';
+import HeatValidationActionHelper from './HeatValidationActionHelper.js';
+import {errorLevels, nodeFilters} from './HeatValidationConstants.js';
+
+export const mapStateToProps = ({softwareProduct: {softwareProductAttachments: {heatValidation}}}) => {
+ let {attachmentsTree, selectedNode, errorList} = heatValidation;
+ let currentErrors = [], currentWarnings = [];
+ if (errorList) {
+ for (let i = 0 ; i < errorList.length ; i++) {
+ if (errorList[i].level === errorLevels.ERROR && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) {
+ currentErrors[currentErrors.length] = errorList[i];
+ }
+ if (errorList[i].level === errorLevels.WARNING && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) {
+ currentWarnings[currentWarnings.length] = errorList[i];
+ }
+ }
+ }
+ return {
+ attachmentsTree,
+ selectedNode,
+ errorList,
+ currentErrors,
+ currentWarnings
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+ toggleExpanded: (path) => HeatValidationActionHelper.toggleExpanded(dispatch, {path}),
+ onSelectNode: (nodeName) => HeatValidationActionHelper.onSelectNode(dispatch, {nodeName}),
+ onDeselectNode: () => HeatValidationActionHelper.onDeselectNode(dispatch)
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(HeatValidationView);
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js
new file mode 100644
index 0000000000..73366c20cc
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js
@@ -0,0 +1,39 @@
+/*!
+ * 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} from './HeatValidationConstants.js';
+
+export default {
+
+ toggleExpanded(dispatch, {path}) {
+ dispatch({
+ type: actionTypes.TOGGLE_EXPANDED,
+ path
+ });
+ },
+
+ onSelectNode(dispatch, {nodeName}) {
+ dispatch({
+ type: actionTypes.SELECTED_NODE,
+ nodeName
+ });
+ },
+
+ onDeselectNode(dispatch) {
+ dispatch({
+ type: actionTypes.UNSELECTED_NODE
+ });
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js
new file mode 100644
index 0000000000..f783fe6482
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js
@@ -0,0 +1,57 @@
+/*!
+ * 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';
+import i18n from 'nfvo-utils/i18n/i18n.js';
+
+export const actionTypes = keyMirror({
+ TOGGLE_EXPANDED: null,
+ SELECTED_NODE: null,
+ UNSELECTED_NODE: null
+});
+
+export const errorTypes = keyMirror({
+ MISSING_FILE_IN_ZIP: i18n('missing file in zip'),
+ MISSING_FILE_IN_MANIFEST: i18n('missing file in manifest'),
+ MISSING_OR_ILLEGAL_FILE_TYPE_IN_MANIFEST: i18n('missing or illegal file type in manifest'),
+ FILE_IS_YML_WITHOUT_YML_EXTENSION: i18n('file is defined as a heat file but it doesn\'t have .yml or .yaml extension'),
+ FILE_IS_ENV_WITHOUT_ENV_EXTENSION: i18n('file is defined as an env file but it doesn\'t have .env extension'),
+ ILLEGAL_YAML_FILE_CONTENT: i18n('illegal yaml file content'),
+ ILLEGAL_HEAT_YAML_FILE_CONTENT: i18n('illegal HEAT yaml file content'),
+ MISSING_FILE_NAME_IN_MANIFEST: i18n('a file is written in manifest without file name'),
+ MISSING_ENV_FILE_IN_ZIP: i18n('missing env file in zip'),
+ ARTIFACT_NOT_IN_USE: i18n('artifact not in use')
+});
+
+export const errorLevels = keyMirror({
+ WARNING: 'WARNING',
+ ERROR: 'ERROR'
+});
+export const nodeFilters = keyMirror({
+ ALL: 'All'
+});
+export const nodeTypes = keyMirror({
+ heat: i18n('Heat'),
+ volume: i18n('Volume'),
+ network: i18n('Network'),
+ artifact: i18n('Artifact'),
+ env: i18n('Environment'),
+ other: i18n('')
+});
+
+export const mouseActions = keyMirror({
+ MOUSE_BUTTON_CLICK: 0
+});
+
diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js
new file mode 100644
index 0000000000..f0c10ed457
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js
@@ -0,0 +1,196 @@
+/*!
+ * 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 as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
+import {actionTypes, nodeFilters} from './HeatValidationConstants.js';
+
+const mapVolumeData = ({fileName, env, errors}) => ({
+ name: fileName,
+ expanded: true,
+ type: 'volume',
+ children: env && [{
+ name: env.fileName,
+ errors: env.errors,
+ type: 'env'
+ }],
+ errors
+});
+
+const mapNetworkData = ({fileName, env, errors}) => ({
+ name: fileName,
+ expanded: true,
+ type: 'network',
+ children: env && [{
+ name: env.fileName,
+ errors: env.errors,
+ type: 'env'
+ }],
+ errors
+});
+
+const mapArtifactsData = ({fileName, errors}) => ({
+ name: fileName,
+ type: 'artifact',
+ errors
+});
+
+const mapOtherData = ({fileName, errors}) => ({
+ name: fileName,
+ type: 'other',
+ errors
+});
+
+
+const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, other}) => ({
+ name: fileName,
+ expanded: true,
+ type: 'heat',
+ errors,
+ children: [
+ ...(volume ? volume.map(mapVolumeData) : []),
+ ...(network ? network.map(mapNetworkData) : []),
+ ...(env ? [{
+ name: env.fileName,
+ errors: env.errors,
+ type: 'env'
+ }] : []),
+ ...(artifacts ? artifacts.map(mapArtifactsData) : []),
+ ...(other ? other.map(mapOtherData) : []),
+ ...(nested ? nested.map(mapHeatData) : [])
+ ]
+});
+
+function createErrorList(node, parent, deep = 0, errorList = []) {
+ if (node.errors) {
+ errorList.push(...node.errors.map((error) => ({
+ level: error.level,
+ errorMessage: error.message,
+ name: node.name,
+ hasParent: deep > 2,
+ parentName: parent.name,
+ type: node.type,
+ })));
+ }
+ if (node.children && node.children.length) {
+ node.children.map((child) => createErrorList(child, node, deep + 1, errorList));
+ }
+ return errorList;
+}
+
+const mapValidationDataToTree = validationData => {
+ let {heat, volume, network, artifacts, other} = validationData.importStructure || {};
+ return {
+ children: [
+ {
+ name: 'HEAT',
+ expanded: true,
+ type: 'heat',
+ header: true,
+ children: (heat ? heat.map(mapHeatData) : [])
+ },
+ ...(artifacts ? [{
+ name: 'artifacts',
+ expanded: true,
+ type: 'artifact',
+ children: (artifacts ? artifacts.map(mapArtifactsData) : [])
+ }] : []),
+ ...(network ? [{
+ name: 'networks',
+ expanded: true,
+ type: 'network',
+ children: (network ? network.map(mapNetworkData) : []),
+ }] : []),
+ ...(volume ? [{
+ name: 'volume',
+ expanded: true,
+ type: 'volume',
+ children: (volume ? volume.map(mapVolumeData) : []),
+ }] : []),
+ ...(other ? [{
+ name: 'other',
+ expanded: true,
+ type: 'other',
+ children: (other ? other.map(mapOtherData) : []),
+ }] : [])
+ ]
+ };
+};
+
+const toggleExpanded = (node, path) => {
+ let newNode = {...node};
+ if (path.length === 0) {
+ newNode.expanded = !node.expanded;
+ } else {
+ let index = path[0];
+ newNode.children = [
+ ...node.children.slice(0, index),
+ toggleExpanded(node.children[index], path.slice(1)),
+ ...node.children.slice(index + 1)
+ ];
+ }
+ return newNode;
+};
+
+const expandSelected = (node, selectedNode) => {
+ let shouldExpand = node.name === selectedNode;
+ let children = node.children && node.children.map(child => {
+ let {shouldExpand: shouldExpandChild, node: newChild} = expandSelected(child, selectedNode);
+ shouldExpand = shouldExpand || shouldExpandChild;
+ return newChild;
+ });
+
+ return {
+ node: {
+ ...node,
+ expanded: node.expanded || shouldExpand,
+ children
+ },
+ shouldExpand
+ };
+};
+
+export default (state = {attachmentsTree: {}}, action) => {
+ switch (action.type) {
+ case softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED:
+ let currentSoftwareProduct = action.response;
+ let attachmentsTree = currentSoftwareProduct.validationData ? mapValidationDataToTree(currentSoftwareProduct.validationData) : {};
+ let errorList = createErrorList(attachmentsTree);
+ return {
+ ...state,
+ attachmentsTree,
+ errorList,
+ selectedNode: nodeFilters.ALL
+ };
+ case actionTypes.TOGGLE_EXPANDED:
+ return {
+ ...state,
+ attachmentsTree: toggleExpanded(state.attachmentsTree, action.path)
+ };
+ case actionTypes.SELECTED_NODE:
+ let selectedNode = action.nodeName;
+ return {
+ ...state,
+ attachmentsTree: expandSelected(state.attachmentsTree, selectedNode).node,
+ selectedNode
+ };
+ case actionTypes.UNSELECTED_NODE:
+ return {
+ ...state,
+ selectedNode: nodeFilters.ALL
+ };
+ default:
+ return state;
+ }
+};
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;