diff options
Diffstat (limited to 'ui-react/src')
-rw-r--r-- | ui-react/src/LoopUI.js | 155 | ||||
-rw-r--r-- | ui-react/src/__snapshots__/LoopUI.test.js.snap | 4 | ||||
-rw-r--r-- | ui-react/src/__snapshots__/OnapClamp.test.js.snap | 4 | ||||
-rw-r--r-- | ui-react/src/components/dialogs/PerformActions.js | 52 | ||||
-rw-r--r-- | ui-react/src/components/dialogs/PerformActions.test.js | 8 | ||||
-rw-r--r-- | ui-react/src/components/dialogs/Policy/PolicyModal.js | 63 | ||||
-rw-r--r-- | ui-react/src/components/loop_viewer/svg/SvgGenerator.js | 16 | ||||
-rw-r--r-- | ui-react/src/utils/OnapUtils.js | 65 |
8 files changed, 297 insertions, 70 deletions
diff --git a/ui-react/src/LoopUI.js b/ui-react/src/LoopUI.js index 8624726be..0ee6e6e24 100644 --- a/ui-react/src/LoopUI.js +++ b/ui-react/src/LoopUI.js @@ -52,6 +52,7 @@ import PerformAction from './components/dialogs/PerformActions'; import RefreshStatus from './components/dialogs/RefreshStatus'; import DeployLoopModal from './components/dialogs/Loop/DeployLoopModal'; import Alert from 'react-bootstrap/Alert'; +import Spinner from 'react-bootstrap/Spinner'; import { Link } from 'react-router-dom'; @@ -59,6 +60,11 @@ const StyledMainDiv = styled.div` background-color: ${props => props.theme.backgroundColor}; ` +const StyledSpinnerDiv = styled.div` + justify-content: center !important; + display: flex !important; +`; + const ProjectNameStyled = styled.a` vertical-align: middle; padding-left: 30px; @@ -108,7 +114,8 @@ export default class LoopUI extends React.Component { loopName: OnapConstants.defaultLoopName, loopCache: new LoopCache({}), showSucAlert: false, - showFailAlert: false + showFailAlert: false, + busyLoadingCount: 0 }; constructor() { @@ -120,6 +127,9 @@ export default class LoopUI extends React.Component { this.showSucAlert = this.showSucAlert.bind(this); this.showFailAlert = this.showFailAlert.bind(this); this.disableAlert = this.disableAlert.bind(this); + this.setBusyLoading = this.setBusyLoading.bind(this); + this.clearBusyLoading = this.clearBusyLoading.bind(this); + this.isBusyLoading = this.isBusyLoading.bind(this); } componentWillMount() { @@ -191,7 +201,7 @@ export default class LoopUI extends React.Component { renderLoopViewBody() { return ( <LoopViewBodyDivStyled> - <SvgGenerator loopCache={this.state.loopCache} clickable={true} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE}/> + <SvgGenerator loopCache={this.state.loopCache} clickable={true} generatedFrom={SvgGenerator.GENERATED_FROM_INSTANCE} isBusyLoading={this.isBusyLoading}/> <LoopStatus loopCache={this.state.loopCache}/> <LoopLogs loopCache={this.state.loopCache} /> </LoopViewBodyDivStyled> @@ -225,53 +235,160 @@ export default class LoopUI extends React.Component { showFailAlert(message) { this.setState ({ showFailAlert: true, showMessage:message }); } - + disableAlert() { this.setState ({ showSucAlert: false, showFailAlert: false }); } loadLoop(loopName) { + this.setBusyLoading(); LoopService.getLoop(loopName).then(loop => { console.debug("Updating loopCache"); LoopActionService.refreshStatus(loopName).then(data => { this.updateLoopCache(data); + this.clearBusyLoading(); this.props.history.push('/'); }) .catch(error => { this.updateLoopCache(loop); + this.clearBusyLoading(); this.props.history.push('/'); }); }); } + setBusyLoading() { + this.setState((state,props) => ({ busyLoadingCount: ++state.busyLoadingCount })); + } + + clearBusyLoading() { + this.setState((state,props) => ({ busyLoadingCount: --state.busyLoadingCount })); + } + + isBusyLoading() { + if (this.state.busyLoadingCount === 0) { + return false; + } else { + return true; + } + } + closeLoop() { this.setState({ loopCache: new LoopCache({}), loopName: OnapConstants.defaultLoopName }); this.props.history.push('/'); } - render() { - return ( - <StyledMainDiv id="main_div"> + renderRoutes() { + return( + <React.Fragment> <Route path="/uploadToscaPolicyModal" render={(routeProps) => (<UploadToscaPolicyModal {...routeProps} />)} /> <Route path="/viewToscaPolicyModal" render={(routeProps) => (<ViewToscaPolicyModal {...routeProps} />)} /> <Route path="/ViewLoopTemplatesModal" render={(routeProps) => (<ViewLoopTemplatesModal {...routeProps} />)} /> <Route path="/ManageDictionaries" render={(routeProps) => (<ManageDictionaries {...routeProps} />)} /> - <Route path="/policyModal/:policyInstanceType/:policyName" render={(routeProps) => (<PolicyModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} /> - <Route path="/createLoop" render={(routeProps) => (<CreateLoopModal {...routeProps} loadLoopFunction={this.loadLoop} />)} /> - <Route path="/openLoop" render={(routeProps) => (<OpenLoopModal {...routeProps} loadLoopFunction={this.loadLoop} />)} /> - <Route path="/loopProperties" render={(routeProps) => (<LoopPropertiesModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} /> - <Route path="/modifyLoop" render={(routeProps) => (<ModifyLoopModal {...routeProps} loopCache={this.getLoopCache()} loadLoopFunction={this.loadLoop}/>)} /> + + <Route path="/policyModal/:policyInstanceType/:policyName" render={(routeProps) => (<PolicyModal {...routeProps} + loopCache={this.getLoopCache()} + loadLoopFunction={this.loadLoop}/>)} + /> + <Route path="/createLoop" render={(routeProps) => (<CreateLoopModal {...routeProps} + loadLoopFunction={this.loadLoop} />)} + /> + <Route path="/openLoop" render={(routeProps) => (<OpenLoopModal {...routeProps} + loadLoopFunction={this.loadLoop} />)} + /> + <Route path="/loopProperties" render={(routeProps) => (<LoopPropertiesModal {...routeProps} + loopCache={this.getLoopCache()} + loadLoopFunction={this.loadLoop}/>)} + /> + <Route path="/modifyLoop" render={(routeProps) => (<ModifyLoopModal {...routeProps} + loopCache={this.getLoopCache()} + loadLoopFunction={this.loadLoop}/>)} + /> <Route path="/userInfo" render={(routeProps) => (<UserInfoModal {...routeProps} />)} /> <Route path="/closeLoop" render={this.closeLoop} /> - <Route path="/submit" render={(routeProps) => (<PerformAction {...routeProps} loopAction="submit" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} /> - <Route path="/stop" render={(routeProps) => (<PerformAction {...routeProps} loopAction="stop" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} /> - <Route path="/restart" render={(routeProps) => (<PerformAction {...routeProps} loopAction="restart" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} /> - <Route path="/delete" render={(routeProps) => (<PerformAction {...routeProps} loopAction="delete" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} /> - <Route path="/undeploy" render={(routeProps) => (<PerformAction {...routeProps} loopAction="undeploy" loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} /> - <Route path="/deploy" render={(routeProps) => (<DeployLoopModal {...routeProps} loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} /> - <Route path="/refreshStatus" render={(routeProps) => (<RefreshStatus {...routeProps} loopCache={this.getLoopCache()} updateLoopFunction={this.updateLoopCache} showSucAlert={this.showSucAlert} showFailAlert={this.showFailAlert}/>)} /> - <GlobalClampStyle /> + + <Route path="/submit" render={(routeProps) => (<PerformAction {...routeProps} + loopAction="submit" + loopCache={this.getLoopCache()} + updateLoopFunction={this.updateLoopCache} + showSucAlert={this.showSucAlert} + showFailAlert={this.showFailAlert} + setBusyLoading={this.setBusyLoading} + clearBusyLoading={this.clearBusyLoading}/>)} + /> + <Route path="/stop" render={(routeProps) => (<PerformAction {...routeProps} + loopAction="stop" + loopCache={this.getLoopCache()} + updateLoopFunction={this.updateLoopCache} + showSucAlert={this.showSucAlert} + showFailAlert={this.showFailAlert} + setBusyLoading={this.setBusyLoading} + clearBusyLoading={this.clearBusyLoading}/>)} + /> + <Route path="/restart" render={(routeProps) => (<PerformAction {...routeProps} + loopAction="restart" + loopCache={this.getLoopCache()} + updateLoopFunction={this.updateLoopCache} + showSucAlert={this.showSucAlert} + showFailAlert={this.showFailAlert} + setBusyLoading={this.setBusyLoading} + clearBusyLoading={this.clearBusyLoading}/>)} + /> + <Route path="/delete" render={(routeProps) => (<PerformAction {...routeProps} + loopAction="delete" + loopCache={this.getLoopCache()} + updateLoopFunction={this.updateLoopCache} + showSucAlert={this.showSucAlert} + showFailAlert={this.showFailAlert} + setBusyLoading={this.setBusyLoading} + clearBusyLoading={this.clearBusyLoading}/>)} + /> + <Route path="/undeploy" render={(routeProps) => (<PerformAction {...routeProps} + loopAction="undeploy" + loopCache={this.getLoopCache()} + updateLoopFunction={this.updateLoopCache} + showSucAlert={this.showSucAlert} + showFailAlert={this.showFailAlert} + setBusyLoading={this.setBusyLoading} + clearBusyLoading={this.clearBusyLoading}/>)} + /> + <Route path="/deploy" render={(routeProps) => (<DeployLoopModal {...routeProps} + loopCache={this.getLoopCache()} + updateLoopFunction={this.updateLoopCache} + showSucAlert={this.showSucAlert} + showFailAlert={this.showFailAlert}/>)} + /> + <Route path="/refreshStatus" render={(routeProps) => (<RefreshStatus {...routeProps} + loopCache={this.getLoopCache()} + updateLoopFunction={this.updateLoopCache} + showSucAlert={this.showSucAlert} + showFailAlert={this.showFailAlert}/>)} + /> + </React.Fragment> + ); + } + + renderSpinner() { + if (this.isBusyLoading()) { + return ( + <StyledSpinnerDiv> + <Spinner animation="border" role="status"> + <span className="sr-only">Loading...</span> + </Spinner> + </StyledSpinnerDiv> + ); + } else { + return (<div></div>); + } + } + + render() { + return ( + <StyledMainDiv id="main_div"> + <GlobalClampStyle /> + {this.renderRoutes()} + {this.renderSpinner()} {this.renderAlertBar()} {this.renderNavBar()} {this.renderLoopViewer()} diff --git a/ui-react/src/__snapshots__/LoopUI.test.js.snap b/ui-react/src/__snapshots__/LoopUI.test.js.snap index 2dfa48091..cae9182ff 100644 --- a/ui-react/src/__snapshots__/LoopUI.test.js.snap +++ b/ui-react/src/__snapshots__/LoopUI.test.js.snap @@ -4,6 +4,7 @@ exports[`Verify LoopUI Test the render method 1`] = ` <styled.div id="main_div" > + <GlobalStyleComponent /> <Route path="/uploadToscaPolicyModal" render={[Function]} @@ -76,7 +77,7 @@ exports[`Verify LoopUI Test the render method 1`] = ` path="/refreshStatus" render={[Function]} /> - <GlobalStyleComponent /> + <div /> <div> <Alert closeLabel="Close alert" @@ -166,6 +167,7 @@ exports[`Verify LoopUI Test the render method 1`] = ` <withRouter(SvgGenerator) clickable={true} generatedFrom="INSTANCE" + isBusyLoading={[Function]} loopCache={ LoopCache { "loopJsonCache": Object {}, diff --git a/ui-react/src/__snapshots__/OnapClamp.test.js.snap b/ui-react/src/__snapshots__/OnapClamp.test.js.snap index 56d022fc6..d4573b3d1 100644 --- a/ui-react/src/__snapshots__/OnapClamp.test.js.snap +++ b/ui-react/src/__snapshots__/OnapClamp.test.js.snap @@ -31,6 +31,7 @@ exports[`Verify OnapClamp Test the render method 1`] = ` <styled.div id="main_div" > + <GlobalStyleComponent /> <Route path="/uploadToscaPolicyModal" render={[Function]} @@ -103,7 +104,7 @@ exports[`Verify OnapClamp Test the render method 1`] = ` path="/refreshStatus" render={[Function]} /> - <GlobalStyleComponent /> + <div /> <div> <Alert closeLabel="Close alert" @@ -191,6 +192,7 @@ exports[`Verify OnapClamp Test the render method 1`] = ` <withRouter(SvgGenerator) clickable={true} generatedFrom="INSTANCE" + isBusyLoading={[Function]} loopCache={ LoopCache { "loopJsonCache": Object {}, diff --git a/ui-react/src/components/dialogs/PerformActions.js b/ui-react/src/components/dialogs/PerformActions.js index cf5a3c20e..f6001e21f 100644 --- a/ui-react/src/components/dialogs/PerformActions.js +++ b/ui-react/src/components/dialogs/PerformActions.js @@ -22,24 +22,19 @@ */ import React from 'react'; import LoopActionService from '../../api/LoopActionService'; -import Spinner from 'react-bootstrap/Spinner' -import styled from 'styled-components'; -const StyledSpinnerDiv = styled.div` - justify-content: center !important; - display: flex !important; -`; export default class PerformActions extends React.Component { state = { loopName: this.props.loopCache.getLoopName(), loopAction: this.props.loopAction }; + constructor(props, context) { super(props, context); - this.refreshStatus = this.refreshStatus.bind(this); } + componentWillReceiveProps(newProps) { this.setState({ loopName: newProps.loopCache.getLoopName(), @@ -51,35 +46,50 @@ export default class PerformActions extends React.Component { const action = this.state.loopAction; const loopName = this.state.loopName; - LoopActionService.performAction(loopName, action).then(pars => { + if (action === 'delete') { + if (window.confirm('You are about to remove Control Loop Model "' + loopName + + '". Select OK to continue with deletion or Cancel to keep the model.') === false) { + return; + } + } + + this.props.setBusyLoading(); // Alert top level to start block user clicks + + LoopActionService.performAction(loopName, action) + .then(pars => { this.props.showSucAlert("Action " + action + " successfully performed"); - // refresh status and update loop logs - this.refreshStatus(loopName); + if (action === 'delete') { + this.props.updateLoopFunction(null); + this.props.history.push('/'); + } else { + // refresh status and update loop logs + this.refreshStatus(loopName); + } }) .catch(error => { this.props.showFailAlert("Action " + action + " failed"); // refresh status and update loop logs this.refreshStatus(loopName); - }); - + }) + .finally(() => this.props.clearBusyLoading()); } refreshStatus(loopName) { - LoopActionService.refreshStatus(loopName).then(data => { + + this.props.setBusyLoading(); + + LoopActionService.refreshStatus(loopName) + .then(data => { this.props.updateLoopFunction(data); this.props.history.push('/'); }) - .catch(error => { + .catch(error => { this.props.history.push('/'); - }); + }) + .finally(() => this.props.clearBusyLoading()); } render() { - return ( - <StyledSpinnerDiv> - <Spinner animation="border" role="status"> - </Spinner> - </StyledSpinnerDiv> - ); + return null; } } diff --git a/ui-react/src/components/dialogs/PerformActions.test.js b/ui-react/src/components/dialogs/PerformActions.test.js index b833a929d..c91c2f675 100644 --- a/ui-react/src/components/dialogs/PerformActions.test.js +++ b/ui-react/src/components/dialogs/PerformActions.test.js @@ -38,6 +38,8 @@ describe('Verify PerformActions', () => { const updateLoopFunction = jest.fn(); const showSucAlert = jest.fn(); const showFailAlert = jest.fn(); + const setBusyLoading = jest.fn(); + const clearBusyLoading = jest.fn(); LoopActionService.refreshStatus = jest.fn().mockImplementation(() => { return Promise.resolve({ @@ -47,7 +49,7 @@ describe('Verify PerformActions', () => { }); }); const component = shallow(<PerformActions loopCache={loopCache} - loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} />) + loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} setBusyLoading={setBusyLoading} clearBusyLoading={clearBusyLoading}/>) await flushPromises(); component.update(); @@ -60,6 +62,8 @@ describe('Verify PerformActions', () => { const updateLoopFunction = jest.fn(); const showSucAlert = jest.fn(); const showFailAlert = jest.fn(); + const setBusyLoading = jest.fn(); + const clearBusyLoading = jest.fn(); LoopActionService.performAction = jest.fn().mockImplementation(() => { return Promise.resolve({ @@ -76,7 +80,7 @@ describe('Verify PerformActions', () => { }); }); const component = shallow(<PerformActions loopCache={loopCache} - loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} />) + loopAction="submit" history={historyMock} updateLoopFunction={updateLoopFunction} showSucAlert={showSucAlert} showFailAlert={showFailAlert} setBusyLoading={setBusyLoading} clearBusyLoading={clearBusyLoading}/>) await flushPromises(); component.update(); diff --git a/ui-react/src/components/dialogs/Policy/PolicyModal.js b/ui-react/src/components/dialogs/Policy/PolicyModal.js index d3b427396..6b1ebe178 100644 --- a/ui-react/src/components/dialogs/Policy/PolicyModal.js +++ b/ui-react/src/components/dialogs/Policy/PolicyModal.js @@ -34,11 +34,16 @@ import LoopCache from '../../../api/LoopCache'; import JSONEditor from '@json-editor/json-editor'; import Alert from 'react-bootstrap/Alert'; import OnapConstant from '../../../utils/OnapConstants'; +import OnapUtils from '../../../utils/OnapUtils'; const ModalStyled = styled(Modal)` background-color: transparent; ` +const DivWhiteSpaceStyled = styled.div` + white-space: pre; +` + export default class PolicyModal extends React.Component { state = { @@ -70,42 +75,49 @@ export default class PolicyModal extends React.Component { this.renderPdpGroupDropDown = this.renderPdpGroupDropDown.bind(this); this.renderOpenLoopMessage = this.renderOpenLoopMessage.bind(this); this.renderModalTitle = this.renderModalTitle.bind(this); + this.readOnly = props.readOnly !== undefined ? props.readOnly : false; } handleSave() { - var errors = this.state.jsonEditor.validate(); var editorData = this.state.jsonEditor.getValue(); + var errors = this.state.jsonEditor.validate(); + errors = errors.concat(this.customValidation(editorData, this.state.loopCache.getTemplateName())); if (errors.length !== 0) { console.error("Errors detected during policy data validation ", errors); this.setState({ - showFailAlert: true, - showMessage: "Errors detected during policy data validation " + errors - }); + showFailAlert: true, + showMessage: 'Errors detected during policy data validation:\n' + OnapUtils.jsonEditorErrorFormatter(errors) + }); return; } else { console.info("NO validation errors found in policy data"); if (this.state.policyInstanceType === OnapConstant.microServiceType) { - this.state.loopCache.updateMicroServiceProperties(this.state.policyName, editorData); - this.state.loopCache.updateMicroServicePdpGroup(this.state.policyName, this.state.chosenPdpGroup, this.state.chosenPdpSubgroup); - LoopService.setMicroServiceProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getMicroServiceForName(this.state.policyName)).then(resp => { - this.setState({ show: false }); - this.props.history.push('/'); - this.props.loadLoopFunction(this.state.loopCache.getLoopName()); - }); + this.state.loopCache.updateMicroServiceProperties(this.state.policyName, editorData); + this.state.loopCache.updateMicroServicePdpGroup(this.state.policyName, this.state.chosenPdpGroup, this.state.chosenPdpSubgroup); + LoopService.setMicroServiceProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getMicroServiceForName(this.state.policyName)).then(resp => { + this.setState({ show: false }); + this.props.history.push('/'); + this.props.loadLoopFunction(this.state.loopCache.getLoopName()); + }); } else if (this.state.policyInstanceType === OnapConstant.operationalPolicyType) { this.state.loopCache.updateOperationalPolicyProperties(this.state.policyName, editorData); this.state.loopCache.updateOperationalPolicyPdpGroup(this.state.policyName, this.state.chosenPdpGroup, this.state.chosenPdpSubgroup); LoopService.setOperationalPolicyProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getOperationalPolicies()).then(resp => { this.setState({ show: false }); - this.props.history.push('/'); + this.props.history.push('/'); this.props.loadLoopFunction(this.state.loopCache.getLoopName()); }); } } } + customValidation(editorData, templateName) { + // method for sub-classes to override with customized validation + return []; + } + handleClose() { this.setState({ show: false }); this.props.history.push('/'); @@ -115,6 +127,15 @@ export default class PolicyModal extends React.Component { this.renderJsonEditor(); } + componentDidUpdate() { + if (this.state.showSucAlert === true || this.state.showFailAlert === true) { + let modalElement = document.getElementById("policyModal") + if (modalElement) { + modalElement.scrollTo(0, 0); + } + } + } + createJsonEditor(toscaModel, editorData) { JSONEditor.defaults.themes.myBootstrap4 = JSONEditor.defaults.themes.bootstrap4.extend({ getTab: function(text,tabId) { @@ -313,12 +334,16 @@ export default class PolicyModal extends React.Component { <Modal.Header closeButton> {this.renderModalTitle()} </Modal.Header> - <Alert variant="success" show={this.state.showSucAlert} onClose={this.disableAlert} dismissible> - {this.state.showMessage} - </Alert> - <Alert variant="danger" show={this.state.showFailAlert} onClose={this.disableAlert} dismissible> - {this.state.showMessage} - </Alert> + <Alert variant="success" show={this.state.showSucAlert} onClose={this.disableAlert} dismissible> + <DivWhiteSpaceStyled> + {this.state.showMessage} + </DivWhiteSpaceStyled> + </Alert> + <Alert variant="danger" show={this.state.showFailAlert} onClose={this.disableAlert} dismissible> + <DivWhiteSpaceStyled> + {this.state.showMessage} + </DivWhiteSpaceStyled> + </Alert> <Modal.Body> {this.renderOpenLoopMessage()} <div id="editor" /> @@ -330,4 +355,4 @@ export default class PolicyModal extends React.Component { </ModalStyled> ); } -}
\ No newline at end of file +} diff --git a/ui-react/src/components/loop_viewer/svg/SvgGenerator.js b/ui-react/src/components/loop_viewer/svg/SvgGenerator.js index d718c2e44..7070455e7 100644 --- a/ui-react/src/components/loop_viewer/svg/SvgGenerator.js +++ b/ui-react/src/components/loop_viewer/svg/SvgGenerator.js @@ -70,13 +70,15 @@ class SvgGenerator extends React.Component { } handleSvgClick(event) { - if (this.state.clickable) { - console.debug("svg click event received"); - var elementName = event.target.parentNode.getAttribute('policyId'); - console.info("SVG element clicked", elementName); - if (elementName !== null) { - this.props.history.push("/policyModal/"+event.target.parentNode.getAttribute('policyType')+"/"+elementName); - } + console.debug("svg click event received"); + if (this.state.clickable) { + var elementName = event.target.parentNode.getAttribute('policyId'); + console.info("SVG element clicked", elementName); + // Only allow movement to policy editing IF there busyLoadingCOunt is 0, + // meaning we are not waiting for refreshStatus to complete, for example + if (elementName !== null && !this.props.isBusyLoading()) { + this.props.history.push("/policyModal/"+event.target.parentNode.getAttribute('policyType')+"/"+elementName); + } } } diff --git a/ui-react/src/utils/OnapUtils.js b/ui-react/src/utils/OnapUtils.js new file mode 100644 index 000000000..316a0d65f --- /dev/null +++ b/ui-react/src/utils/OnapUtils.js @@ -0,0 +1,65 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP CLAMP + * ================================================================================ + * Copyright (C) 2020 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============================================ + * =================================================================== + * + */ + +export default class OnapUtils { + + constructor() { + this.clickBlocked = false; + } + + static jsonEditorErrorFormatter(errors) { + + let messages = []; + let messagesOutputString = null; + + // errors is an array of JSON Editor "error" objects, where each + // object looks like this: + + // { + // message: "Please populate the required property "Threshold"" + // path: "root.signatures.0" + // property: "required" + // } + + // In this function we concatenate all the messages, removing any duplicates, + // and adding a newline between each message. The result returned is a single + // string that can be displayed to the user in an alert message + + if (!Array.isArray(errors)) { + console.error('jsoneEditorErrorFormatter was passed a non-array argument'); + } else { + for (let ii=0; ii < errors.length; ++ii) { + if (!messages.includes(errors[ii].message)) { + messages.push(errors[ii].message); + if (messagesOutputString) { + messagesOutputString += '\n' + errors[ii].message; + } else { + messagesOutputString = errors[ii].message; + } + } + } + } + + return messagesOutputString; + } +} |