diff options
author | wr148d <wr148d@att.com> | 2021-01-15 15:32:00 -0500 |
---|---|---|
committer | wr148d <wr148d@att.com> | 2021-02-11 09:47:17 -0500 |
commit | 5ee7367a101143715c2869d72ea4a6fbf55f5af6 (patch) | |
tree | 84bf43601c0cce4fb37b5b3b494e113c96d5591e /src/generic-components | |
parent | ddc05d4ea0254b427fea6ec80e2b03950eeca4ce (diff) |
Updated Sparky to add ECOMP functionality Browse, Specialized Search, BYOQ, and the Builder FE Updates
Issue-ID: AAI-3250
Change-Id: I576e37f77f7e9b40d72e4a5e7de645e9f62bc7d2
Signed-off-by: wr148d <wr148d@att.com>
Diffstat (limited to 'src/generic-components')
-rw-r--r-- | src/generic-components/DownloadRangeModel.jsx | 236 | ||||
-rw-r--r-- | src/generic-components/InfoToggle.jsx | 106 | ||||
-rw-r--r-- | src/generic-components/MultiSelectDropDown.jsx | 56 | ||||
-rw-r--r-- | src/generic-components/OutputToggle.jsx | 53 | ||||
-rw-r--r-- | src/generic-components/OutputVisualization.jsx | 331 | ||||
-rw-r--r-- | src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx | 64 | ||||
-rw-r--r-- | src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBarConstants.js | 1 | ||||
-rw-r--r-- | src/generic-components/filter/Filter.jsx | 270 | ||||
-rw-r--r-- | src/generic-components/filter/components/AddFilters.jsx | 27 | ||||
-rw-r--r-- | src/generic-components/filter/components/ClearFilter.jsx | 27 | ||||
-rw-r--r-- | src/generic-components/filter/components/FilterTypes.jsx | 102 | ||||
-rw-r--r-- | src/generic-components/filter/components/RunFilterQuery.jsx | 56 | ||||
-rw-r--r-- | src/generic-components/filter/components/SelectFilter.jsx | 100 |
13 files changed, 1427 insertions, 2 deletions
diff --git a/src/generic-components/DownloadRangeModel.jsx b/src/generic-components/DownloadRangeModel.jsx new file mode 100644 index 0000000..239ba64 --- /dev/null +++ b/src/generic-components/DownloadRangeModel.jsx @@ -0,0 +1,236 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React, {Component} from 'react'; +import Modal from 'react-bootstrap/lib/Modal'; +import {GlobalExtConstants} from 'utils/GlobalExtConstants.js'; +import Row from 'react-bootstrap/lib/Row'; +import Button from 'react-bootstrap/lib/Button'; +import Spinner from 'utils/SpinnerContainer.jsx'; + +let PAGINATION_CONSTANT = GlobalExtConstants.PAGINATION_CONSTANT; +let PAGINATION_RESULTS = PAGINATION_CONSTANT.RESULTS_PER_PAGE; + +class DownloadRangeModel extends Component { + constructor(props){ + super(props); + let totalPages = Math.ceil(this.props.totalResults/PAGINATION_RESULTS); + let defaultCount = (totalPages < 10)? totalPages : 10; + let defaultPageRange = defaultCount*PAGINATION_RESULTS; + this.state = { + defaultPageCount : defaultCount, + downloadType: 'defaultPage', + totalPages:totalPages, + limitTotalPages:defaultCount, + startPageCount:'', + endPageCount:'', + defaultPageError:false, + downloadPageRangeError:false, + defaultPageErrorMsg:'', + downloadPageRangeErrorMsg:'', + showDownloadResultsModal:this.props.showDownloadResultsModal, + triggerDownload:this.props.triggerDownload, + triggerClose:this.props.triggerClose, + totalResults: this.props.totalResults, + errorDownloadResults:this.props.errorDownloadResults, + downloadErrorMsg:this.props.downloadErrorMsg, + pageRange:defaultPageRange, + enableBusyFeedback: this.props.enableModelBusyFeedback + }; + }; + componentDidMount(){ + console.log('DownloadRange component mount'); + }; + componentWillReceiveProps(nextProps){ + console.log('DownloadRange component componentWillReceiveProps',nextProps); + let totalPages = Math.ceil(nextProps.totalResults/PAGINATION_RESULTS); + let defaultCount = (totalPages < 10)? totalPages : 10; + let defaultPageRange = defaultCount*PAGINATION_RESULTS; + this.setState({ + showDownloadResultsModal:nextProps.showDownloadResultsModal, + errorDownloadResults:nextProps.errorDownloadResults, + downloadErrorMsg:nextProps.downloadErrorMsg, + enableBusyFeedback: nextProps.enableModelBusyFeedback, + totalPages:totalPages, + limitTotalPages:defaultCount, + defaultPageCount: defaultCount + }); + } + renderError = (errorMsg) => { + return( + <Row className='show-grid topBottomMargin'> + <span className='label badge-pill label-danger topBottomMargin pre-wrap-text'><strong>Error</strong>: {errorMsg}</span> + </Row> + ); + }; + onInputDataChange = (event) =>{ + let name=event.target.name; + let value= (event.target.value !== '')? parseInt(event.target.value): ''; + this.onDataValidate(name,value); + } + onDataValidate =(name,value) =>{ + console.log('DownloadRangeModel onDataValidate>>>>>>',this.state); + let pageCount=1; + let msg=''; + let totalCount = 0; + let totalResultsCount=0; + if(name === 'defaultPageCount'){ + if(isNaN(value)){ + msg = 'Please enter valid input as Number'; + }else if(value <= 0){ + msg = 'Please enter valid page count From 1 to ' + this.state.limitTotalPages; + }else if(value > this.state.limitTotalPages ){ + msg = 'Please enter valid page count From 1 to ' + this.state.limitTotalPages+'.The maximum download is limited to '+this.state.limitTotalPages +' pages at a time'; + } + if(msg === ''){ + pageCount=value*PAGINATION_RESULTS; + console.log('Before setting state defaultPageCount>>>>>',value); + this.setState({defaultPageCount:value,pageRange:pageCount,defaultPageError:false,defaultPageErrorMsg:'',disableDownloadBtn:false}); + }else if(msg !== ''){ + this.setState({defaultPageCount:event.target.value,defaultPageError:true,defaultPageErrorMsg:msg,disableDownloadBtn:true}); + } + }else if(name === 'startPageCount'){ + if(isNaN(value)){ + msg = 'Please enter valid input as Number'; + } + if(msg === ''){ + console.log('Before setting state startPageCount>>>>>',value); + this.setState({startPageCount:value,downloadPageRangeError:false,downloadPageRangeErrorMsg:'',disableDownloadBtn:false},function(){this.onDataValidate('rangeValidation')}.bind(this)); + }else{ + this.setState({downloadPageRangeError:true,downloadPageRangeErrorMsg:msg,disableDownloadBtn:true}); + } + }else if(name === 'endPageCount'){ + if(isNaN(value)){ + msg = 'Please enter valid input as Number'; + } + if(msg === ''){ + console.log('Before setting state endPageCount>>>>>',value); + this.setState({endPageCount: value,downloadPageRangeError: false, downloadPageRangeErrorMsg: '',disableDownloadBtn:false},function(){this.onDataValidate('rangeValidation')}.bind(this)); + }else{ + this.setState({downloadPageRangeError:true,downloadPageRangeErrorMsg:msg,disableDownloadBtn:true}); + } + } + if(name === 'rangeValidation'){ + let startCount = this.state.startPageCount; + startCount=(startCount === '')? 0:parseInt(startCount); + let endCount = this.state.endPageCount; + endCount=(endCount === '')? 0:parseInt(endCount); + if(startCount <= 0 || endCount <= 0 || startCount > this.state.totalPages || endCount > this.state.totalPages || startCount > endCount || endCount < startCount){ + msg = 'Please enter a valid page range from 1 to '+ this.state.totalPages +'.The maximum download is limited to '+ this.state.limitTotalPages +' pages at a time'; + }else{ + totalCount = this.state.endPageCount - this.state.startPageCount + 1; + totalResultsCount=totalCount*PAGINATION_RESULTS; + if(totalCount > 10){ + msg = 'The maximum download is limited to '+ this.state.limitTotalPages +' pages at a time. Please adjust your input to continue'; + } + } + if(msg !== ''){ + this.setState({downloadPageRangeError:true,downloadPageRangeErrorMsg:msg,disableDownloadBtn:true}); + }else{ + pageCount=this.state.startPageCount + '-' + this.state.endPageCount; + this.setState({disableDownloadBtn: false, pageRange: pageCount}); + } + } + }; + setSelectTypeOfDownload = (event) =>{ + console.log('DownloadRange:setSelectTypeOfDownload',event.target.value); + let totalPages=parseInt(this.state.totalPages); + let pageCount=0; + if (event.target.value === 'downloadPageRange'){ + this.setState( + {defaultPageCount : (totalPages < 10)? totalPages : 10,startPageCount:'',endPageCount:'',downloadType:event.target.value,downloadPageRangeError:false,defaultPageError:false,disableDownloadBtn:true, downloadErrorMsg:'', errorDownloadResults:false} + ); + }else{ + pageCount= (totalPages < 10)? totalPages*PAGINATION_RESULTS : 10*PAGINATION_RESULTS; + this.setState({defaultPageCount : (totalPages < 10)? totalPages : 10, startPageCount:'',endPageCount:'',pageRange:pageCount,downloadType:event.target.value,downloadPageRangeError:false,defaultPageError:false,disableDownloadBtn:false, downloadErrorMsg:'',errorDownloadResults:false}); + } + } + submitDownloadResults = () =>{ + console.log('DownloadRange:submitDonwloadResults',this.state); + this.props.triggerDownload(this.state.pageRange,true); + } + closeDownloadResults = () =>{ + console.log('DownloadRange:closeDownloadReults'); + this.props.triggerClose(); + } + render(){ + console.log('downloadRange:this.state>>>>>>>>>>>*',this.state); + return( + <div id='downloadPagePane' className='addPadding customDsl'> + <div className='static-modal'> + <Modal show={this.state.showDownloadResultsModal} onHide={this.closeDownloadResults}> + <Modal.Header> + <Modal.Title>Download Results</Modal.Title> + </Modal.Header> + <Modal.Body> + <h4>Your result set has <strong>{this.state.totalPages}</strong> pages</h4> + {this.state.errorDownloadResults && (<div>{this.renderError(this.state.downloadErrorMsg)}</div>)} + <form id='downloadRangeForm' name='downloadRangeForm'> + <div className="radio"> + <label> + <input type="radio" value="defaultPage" + checked={this.state.downloadType === 'defaultPage'} + onChange={(e) => this.setSelectTypeOfDownload(e)} /> + <input type='number' size='8' name='defaultPageCount' + onBlur={(event) => this.onInputDataChange(event)} + placeholder='Default Page Count' + value={this.state.defaultPageCount} + disabled={!(this.state.downloadType === 'defaultPage')} + onChange={(event) => this.onInputDataChange(event)} /> + <span>pages</span> + </label> + {this.state.defaultPageError && (<div>{this.renderError(this.state.defaultPageErrorMsg)}</div>)} + </div> + <div className="radio"> + <label> + <input type="radio" value="downloadPageRange" + checked={this.state.downloadType === 'downloadPageRange'} + onChange={(e) => this.setSelectTypeOfDownload(e)} /> + From + <span><input type='number' size='8' name='startPageCount' + onBlur={(event) => this.onInputDataChange(event)} + placeholder='Start Page' + disabled={!(this.state.downloadType === 'downloadPageRange')} + value={this.state.startPageCount} + onChange={(event) => this.onInputDataChange(event)} /></span> + <span>To</span> + <span><input type='number' size='8' name='endPageCount' + onBlur={(event) => this.onInputDataChange(event)} + placeholder='End Page' + value={this.state.endPageCount} + disabled={!(this.state.downloadType === 'downloadPageRange')} + onChange={(event) => this.onInputDataChange(event)} /></span> + <span>pages</span> + </label> + {this.state.downloadPageRangeError && (<div>{this.renderError(this.state.downloadPageRangeErrorMsg)}</div>)} + </div> + </form> + </Modal.Body> + <Modal.Footer> + <Button onClick={this.closeDownloadResults}>Close</Button> + <Button onClick={this.submitDownloadResults} disabled={this.state.disableDownloadBtn}>Download</Button> + </Modal.Footer> + </Modal> + </div> + </div> + ); + } +} +export default DownloadRangeModel; diff --git a/src/generic-components/InfoToggle.jsx b/src/generic-components/InfoToggle.jsx new file mode 100644 index 0000000..928eea7 --- /dev/null +++ b/src/generic-components/InfoToggle.jsx @@ -0,0 +1,106 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React, { Component } from 'react'; +import Modal from 'react-bootstrap/lib/Modal'; +import Col from 'react-bootstrap/lib/Col'; +import Row from 'react-bootstrap/lib/Row'; +import Tabs from 'react-bootstrap/lib/Tabs'; +import Button from 'react-bootstrap/lib/Button'; +import Tab from 'react-bootstrap/lib/Tab'; +import BootstrapTable from 'react-bootstrap-table-next'; +import {GlobalExtConstants} from 'utils/GlobalExtConstants.js'; + + +class InfoToggle extends Component { + constructor(props){ + console.log(props); + super(props); + this.props = props; + this.state = { + showInfoModal: false + }; + } + openInfoModal = () =>{ + this.setState({showInfoModal:true}); + } + closeInfoModal = () =>{ + this.setState({showInfoModal:false}); + } + getReferenceJson = (reference) =>{ + return require('app/assets/configuration/' + GlobalExtConstants.PATHNAME + '/reference/' + reference + '.json'); + } + render(){ + let tableColumnsList = []; + let tableDataList = []; + let types = this.getReferenceJson('types'); + types.map(type => { + tableColumnsList[type] = []; + tableDataList[type] = this.getReferenceJson(type); + for(var key in tableDataList[type][0]){ + var isHidden = key === 'id'; + tableColumnsList[type].push({dataField: key, text: key, hidden: isHidden }); + } + }); + let tabs=types.map((nodeType,index) => { + return( + <Tab eventKey={nodeType} title={nodeType}> + <BootstrapTable + id={nodeType} + keyField='id' + data={tableDataList[nodeType]} + columns={tableColumnsList[nodeType]} + bordered={ true } + headerClasses='table-header-view' + bootstrap4 striped hover condensed + /> + </Tab> + ) + }); + if (!GlobalExtConstants.INVLIST.IS_ONAP){ + return ( + <div> + <div className='static-modal'> + <Modal show={this.state.showInfoModal} onHide={this.closeInfoModal}> + <Modal.Header> + <Modal.Title>Information</Modal.Title> + </Modal.Header> + <Modal.Body> + <Tabs defaultActiveKey={types[0]} id="multipleTabularView"> + {tabs} + </Tabs> + </Modal.Body> + <Modal.Footer> + <Button onClick={this.closeInfoModal}>Close</Button> + </Modal.Footer> + </Modal> + </div> + <div className='col-xs-1'> + <i className='dsl-hint icon-documents-manual' onClick={this.openInfoModal} ></i> + <pre>Info</pre> + </div> + </div>); + }else{ + return (<span></span>); + } + } +}; + +export default InfoToggle; diff --git a/src/generic-components/MultiSelectDropDown.jsx b/src/generic-components/MultiSelectDropDown.jsx new file mode 100644 index 0000000..e9124a5 --- /dev/null +++ b/src/generic-components/MultiSelectDropDown.jsx @@ -0,0 +1,56 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React, {Component} from 'react'; +import Select from 'react-select'; + +class MultiSelectDropDown extends Component { + constructor(props) { + super(props); + this.state = { + options:[], + displayValue:'Category' + }; + } + componentDidMount(){ + console.log('MultiSelectDropDown component mount'); + }; + componentWillReceiveProps(nextProps){ + console.log('MultiSelectDropDown component componentWillReceiveProps',nextProps); + this.setState({ + options:nextProps.options, + displayValue:nextProps.displayValue, + triggerSelect:nextProps.triggerSelect, + }); + } + render() { + return ( + <Select + className='dropdown-item basic-multi-select' + placeholder={this.state.displayValue} + onChange={this.state.triggerSelect} + options={this.state.options} + isMulti + classNamePrefix="select" + /> + ); + } +} +export default MultiSelectDropDown; diff --git a/src/generic-components/OutputToggle.jsx b/src/generic-components/OutputToggle.jsx new file mode 100644 index 0000000..9d82260 --- /dev/null +++ b/src/generic-components/OutputToggle.jsx @@ -0,0 +1,53 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React from 'react'; +import Col from 'react-bootstrap/lib/Col'; +import Row from 'react-bootstrap/lib/Row'; +import {GlobalExtConstants} from 'utils/GlobalExtConstants.js'; + +let PAGINATION_CONSTANT = GlobalExtConstants.PAGINATION_CONSTANT; + +const OutputToggle = (props) => { + + return ( + <div className="addPaddingSide"> + <Row className='show-grid'> + <Col md={8}> + <button type='button' className={'btn ' + ((props.scope.state.viewName === "CardLayout") ? 'btn-primary' : 'btn-outline-secondary')} value="CardLayout" onClick={(e) => props.scope.setViewName(e)}><i className={'icon-content-gridL ' + ((props.scope.state.viewName === "CardLayout") ? 'tabIconChosen' : 'tabIconNotChosen')} role="img"></i></button> + {!props.cellDisabled && <button type='button' className={'btn ' + ((props.scope.state.viewName === "CellLayout") ? 'btn-primary' : 'btn-outline-secondary')} value="CellLayout" onClick={(e) => props.scope.setViewName(e)}><i className={'icon-content-gridguideL ' + ((props.scope.state.viewName === "CellLayout") ? 'tabIconChosen' : 'tabIconNotChosen')} role="img"></i></button>} + {!props.visualDisabled && <button type='button' className={'btn ' + ((props.scope.state.viewName === "VisualLayout") ? 'btn-primary' : 'btn-outline-secondary')} value="VisualLayout" onClick={(e) => props.scope.setViewName(e)}><i className={'icon-datanetwork-globalnetworkL ' + ((props.scope.state.viewName === "VisualLayout") ? 'tabIconChosen' : 'tabIconNotChosen')} role="img"></i></button>} + </Col> + </Row> + <Row className='show-grid'> + <Col md={8}> + <div className='checkbox'> + <label> + {props.scope.state.viewName !== 'VisualLayout' && <input type='checkbox' className='radio' value={props.scope.state.viewName} name='defaultViewName' checked={props.scope.state.viewName === props.scope.state.defaultViewName} onChange={(e) => props.scope.setDefaultViewName(e)} disabled={props.scope.state.viewName === props.scope.state.defaultViewName}/>} + {props.scope.state.viewName !== 'VisualLayout' && (props.scope.state.viewName === props.scope.state.defaultViewName ? 'Default View' : 'Set as Default View')} + </label> + </div> + </Col> + </Row> + </div> + ); +}; + +export default OutputToggle; diff --git a/src/generic-components/OutputVisualization.jsx b/src/generic-components/OutputVisualization.jsx new file mode 100644 index 0000000..734887d --- /dev/null +++ b/src/generic-components/OutputVisualization.jsx @@ -0,0 +1,331 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React from 'react'; +import * as d3 from "d3"; +import 'd3-selection-multi'; +import {GlobalExtConstants} from 'utils/GlobalExtConstants.js'; + +let INVLIST = GlobalExtConstants.INVLIST; +let PAGINATION_CONSTANT = GlobalExtConstants.PAGINATION_CONSTANT; + +/** + * This function will create a visualization from query outputs + * @param props + * @returns {*} + */ +var populateGraphObject = (nodes, links, data) => { + for(var i = 0; i < data.results.length; i++){ + nodes[data.results[i].url] = data.results[i]; + let nodeType = data.results[i]['node-type']; + nodes[data.results[i].url].weight = 1/((((data.results[i].url).split('\/')).length) - 3); + let splitUrl = (data.results[i].url).split(nodeType + '\/'); + nodes[data.results[i].url].nodeTypeLabel = nodeType; + nodes[data.results[i].url].nodeKeyLabel = splitUrl.pop(); + let tempIconString = ((splitUrl.pop()).split('\/')); + tempIconString.pop(); //pop last off, not needed + let iconString = tempIconString.pop(); + let iconKey = (iconString.replace(/-/g, '')).toUpperCase(); + if(INVLIST.INVENTORYLIST[iconKey] && INVLIST.INVENTORYLIST[iconKey].icon){ + nodes[data.results[i].url].icon = INVLIST.INVENTORYLIST[iconKey].icon; + }else{ + nodes[data.results[i].url].icon = 'icon-datanetwork-serverL'; + } + console.log("icon string: " + nodes[data.results[i].url].icon); + nodes[data.results[i].url].id = data.results[i].url; + for(var j = 0; j < data.results[i]['related-to'].length; j++){ + let linkKey = data.results[i].url + '|' + data.results[i]['related-to'][j].url; + let inverseLinkKey = data.results[i]['related-to'][j].url + '|' + data.results[i].url; + if(!links[linkKey] && !links[inverseLinkKey]){ + links[linkKey] = data.results[i]['related-to'][j]; + links[linkKey].source = data.results[i].url; + links[linkKey].target = data.results[i]['related-to'][j].url; + links[linkKey].weight = 1/((((data.results[i].url).split('\/')).length) - 3); + let subset = (data.results[i]['related-to'][j]['relationship-label']).split(/[\.]+/); + links[linkKey].type = subset[subset.length - 1]; + } + } + } + + for (var key in links) { + if (links.hasOwnProperty(key)) { + console.log(key + " -> " + links[key]); + if(!nodes[links[key].source] || !nodes[links[key].target]){ + delete links[key]; + } + } + } +} +var chart = (chartId, nodesObj, linksObj, rawData, classContext) => { + if(rawData.results.length <= PAGINATION_CONSTANT.RESULTS_PER_PAGE){ + populateGraphObject( nodesObj, linksObj, rawData); + let links = Object.values(linksObj).map(d => Object.create(d)); + let nodes = Object.values(nodesObj).map(d => Object.create(d)); + let colors = d3.scaleOrdinal(d3.schemeCategory10); + + let svg = d3.select('#'+chartId), + width = +svg.attr("width"), + height = +svg.attr("height"), + node, + link, + edgepaths, + edgelabels; + + svg.html(null); + var g = svg.append("g") + .attr("class", "everything"); + g.append("rect") + .attr("width", "100%") + .attr("height", "100%") + .attr("fill", "white"); + + var forceLink = d3 + .forceLink().id(function (d) { + return d.id; + }) + .distance(function (d) { + return 1000 * d.weight; + }) + .strength(1.5); + + var collisionForce = d3.forceCollide(100).strength(1.5).iterations(100); + + var simulation = d3.forceSimulation() + .force("link", forceLink) + .force("charge", d3.forceManyBody().strength(function (d, i) { + var a = i == 0 ? -2000 : -1000; + return a; + }).distanceMin(200).distanceMax(1000)) + .force("center", d3.forceCenter(width / 2, height / 2)) + .force("collisionForce",collisionForce); + + //Zoom functions + function zoom_actions(){ + g.attr("transform", d3.event.transform) + } + //add zoom capabilities + var zoom_handler = d3.zoom() + .on("zoom", zoom_actions); + + zoom_handler(svg); + + update(links, nodes); + + function zoomFit() { + var bounds = g.node().getBBox(); + var parent = g.node().parentElement; + var fullWidth = parent.clientWidth || parent.parentNode.clientWidth, + fullHeight = parent.clientHeight || parent.parentNode.clientHeight; + var width = bounds.width, + height = bounds.height; + var midX = bounds.x + width / 2, + midY = bounds.y + height / 2; + if (width == 0 || height == 0) return; // nothing to fit + var scale = 0.95 / Math.max(width / fullWidth, height / fullHeight); + var translate = [fullWidth / 2 - scale * midX, fullHeight / 2 - scale * midY]; + + console.trace("zoomFit", translate, scale); + + /*zoom_handler.translateTo(g, translate[0], translate[1]) + .scaleTo(g, scale);*/ + } + + function update(links, nodes) { + link = g.selectAll(".link") + .data(links) + .enter() + .append("line") + .attrs({ + 'stroke': '#999', + 'stroke-opacity': .6, + 'stroke-width': '1px', + 'id': function (d, i) {return 'line' + chartId + d.id} + }); + + link.append("title") + .text(function (d) {return d.type;}); + + edgepaths = g.selectAll(".edgepath") + .data(links) + .enter() + .append('path') + .attrs({ + 'class': 'edgepath', + 'fill-opacity': 0, + 'stroke-opacity': 0, + 'id': function (d, i) {return 'edgepath' + chartId + d.id} + }) + .style("pointer-events", "none"); + + /*edgelabels = g.selectAll(".edgelabel") + .data(links) + .enter() + .append('text') + .style("pointer-events", "none") + .attrs({ + 'class': 'edgelabel', + 'id': function (d, i) {return 'edgelabel' + chartId + d.id}, + 'font-size': 8, + 'fill': '#aaa' + }); + + edgelabels.append('textPath') + .attr('xlink:href', function (d, i) {return '#edgepath' + chartId + d.id}) + .style("text-anchor", "middle") + .style("pointer-events", "none") + .attr("startOffset", "50%") + .text(function (d) {return d.type});*/ + + node = g.selectAll(".node") + .data(nodes) + .enter() + .append("g") + .attr("class", "node") + .attr("id", function (d) {return "node" + chartId + (((decodeURIComponent(d.url)).replace(new RegExp('\/', 'g'),'-')).replace(new RegExp(':', 'g'),'-')).replace(new RegExp('\\.', 'g'),'-')}) + .call(d3.drag() + .on("start", dragstarted) + .on("drag", dragged) + .on("end", dragended) + ); + + node.append("svg:foreignObject") + .attr("width", 70) + .attr("height", 70) + .attr("x", -45) + .attr("dy", function (d) {return -1 * (Math.max((Math.round(d.weight * 250)/10), 2));}) + .append("xhtml:span") + .attr("class", function (d) {return d.icon;}) + .style("padding", "10px") + .style("font-size", function (d) {return Math.max(Math.round(d.weight * 250), 20) + "px";}) + .attr("id", function (d) {return "nodeIcon" + chartId + (((decodeURIComponent(d.url)).replace(new RegExp('\/', 'g'),'-')).replace(new RegExp(':', 'g'),'-')).replace(new RegExp('\\.', 'g'),'-')}) + .style("color", '#387dff') + .style("display", "block"); + + + node.append("title") + .text(function (d) {return decodeURIComponent(d.id);}); + + node.append("text") + .attr("dy", 0) + .attr("dx", -10) + .attr('font-size', 10) + .text(function (d) {return d.nodeTypeLabel;}) + .style("text-anchor", "middle"); + + node.append("text") + .attr("dy", function (d) {return (Math.max(Math.round(d.weight * 250) + 15, 55));}) + .attr("dx", -10) + .attr('font-size', 8) + .text(function (d) {return decodeURIComponent(d.nodeKeyLabel);}) + .style("text-anchor", "middle"); + + node.on("dblclick",function(d){ classContext.openNodeModal("test", d.url, d['node-type']) }); + + simulation + .nodes(nodes) + .on("tick", ticked) + .on("end", zoomFit); + + simulation.force("link") + .links(links); + + svg.on("dblclick.zoom", null); + } + + function ticked() { + link + .attr("x1", function (d) {return d.source.x;}) + .attr("y1", function (d) {return d.source.y;}) + .attr("x2", function (d) {return d.target.x;}) + .attr("y2", function (d) {return d.target.y;}); + + node + .attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";}); + + edgepaths.attr('d', function (d) { + return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y; + }); + + /*edgelabels.attr('transform', function (d) { + if (d.target.x < d.source.x) { + let bbox = this.getBBox(); + let rx = bbox.x + bbox.width / 2; + let ry = bbox.y + bbox.height / 2; + return 'rotate(180 ' + rx + ' ' + ry + ')'; + } + else { + return 'rotate(0)'; + } + });*/ + } + + function dragstarted(d) { + simulation.stop(); + } + + function dragged(d) { + d.fx = d3.event.x; + d.fy = d3.event.y; + d.x = d3.event.x; + d.y = d3.event.y; + ticked(); + } + + function dragended(d) { + d.fixed = true; + ticked(); + } + }else{ + let svg = d3.select('#'+chartId), + width = +svg.attr("width"), + height = +svg.attr("height"), + node, + link, + edgepaths, + edgelabels; + let svgHtml = "<foreignObject x=\"0\" y=\"0\" width=\"100%\" height=\"100%\">" + + "<body xmlns=\"http://www.w3.org/1999/xhtml\">" + + "<div class=\"svgbody\">" + + "<h1>Graphical output is limited to " + PAGINATION_CONSTANT.RESULTS_PER_PAGE + + " nodes. Your query returned " + rawData.results.length + + " nodes, which the GUI does not support, please limit your query or if this data" + + " is needed access it through our externally supported APIs." + + "</h1>" + + "</div>"+ + "</body>"+ + "</foreignObject>"; + svg.html(svgHtml); + } +} +const OutputVisualization = (props) => { + if (props.identifier && props.width && props.height && props.overflow) { + return ( + <svg id={props.identifier} width={props.width} height={props.height} overflow={props.overflow}></svg> + ); + + }else{ + return (<p>Graph Configuration Error</p>); + } +}; + +export default OutputVisualization; +export const Visualization = { + chart: chart +} diff --git a/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx b/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx index 47cdc9a..3bc9508 100644 --- a/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx +++ b/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx @@ -25,13 +25,16 @@ import AutoSuggest from 'react-autosuggest'; import Highlighter from 'react-highlight-words'; import debounce from 'lodash.debounce'; import {ButtonGroup} from 'react-bootstrap'; +import Modal from 'react-bootstrap/lib/Modal'; import {Link} from 'react-router-dom'; +import {genericRequest} from 'app/networking/NetworkCalls.js'; import {changeUrlAddress} from 'utils/Routes.js'; import { ICON_CLASS_SEARCH, ICON_CLASS_CLEAR, + ICON_CLASS_HELP, SEARCH_DEBOUNCE_TIME, NO_MATCHES_FOUND, SEARCH_PLACEHOLDER_TEXT @@ -45,6 +48,15 @@ export default class AutoCompleteSearchBar extends Component { suggestionName: PropTypes.string }; + constructor(props) { + console.log(props); + super(props); + this.state = { + helpModalShow: false, + searchable: [] + }; + }; + componentWillMount() { this.debouncedLoadSuggestions = debounce(this.props.onSuggestionsFetchRequested, SEARCH_DEBOUNCE_TIME); @@ -107,9 +119,40 @@ export default class AutoCompleteSearchBar extends Component { onChange: onInputChange }; + let closeHelpModal = () => { + this.setState({helpModalShow: false}); + }; + let showHelpModal = () => { + genericRequest('/schema/searchable', true, 'GET').then(res=>{ + let searchDOM = res.sort(function(a, b) { + var compareA = (a['node-type']).toLowerCase(); + var compareB = (b['node-type']).toLowerCase(); + if(compareA < compareB){ + return -1; + }; + if(compareA > compareB){ + return 1; + }; + return 0; + }).map((prop) => { + return ( + <div><p><strong>{prop['node-type']}:</strong></p><p>{prop['searchable-attributes']}</p></div> + ); + }); + this.setState({searchable: searchDOM, helpModalShow: true}); + }, error => { + console.log(error); + this.setState({searchable: 'An error occurred, please try again later.', helpModalShow: true}); + }).catch(error => { + console.log(error); + this.setState({searchable: 'An error occurred, please try again later.', helpModalShow: true}); + }); + }; + let clearButtonClass = (value.length > 0) ? 'auto-complete-clear-button' : 'auto-complete-clear-button hidden'; + return ( <div className='auto-complete-search'> <AutoSuggest @@ -127,10 +170,12 @@ export default class AutoCompleteSearchBar extends Component { renderSuggestionsContainer={this.renderSuggestionsContainer}/> <ButtonGroup className='auto-complete-search-button-group'> <Button type='submit' className={clearButtonClass} - onClick={onClearSuggestionsTextFieldRequested}> + onClick={onClearSuggestionsTextFieldRequested}> <i className={ICON_CLASS_CLEAR} aria-hidden='true'/> </Button> - + <Button type='submit' className='auto-complete-help-button' onClick={showHelpModal}> + <i className={ICON_CLASS_HELP} aria-hidden='true'/> + </Button> <Button type='submit' className='auto-complete-search-button' onClick={() => { this.newSearchSelected(this.getSelectedSuggestionObj(value, cachedSuggestions), onInvalidSearch, dispatchAnalytics, value); @@ -139,6 +184,21 @@ export default class AutoCompleteSearchBar extends Component { <i className={ICON_CLASS_SEARCH} aria-hidden='true'/> </Button> </ButtonGroup> + <div className='static-modal'> + <Modal show={this.state.helpModalShow} onHide={closeHelpModal}> + <Modal.Header> + <Modal.Title>Searchable Fields</Modal.Title> + </Modal.Header> + <Modal.Body> + <div className='modal-searchable'> + {this.state.searchable} + </div> + </Modal.Body> + <Modal.Footer> + <Button onClick={closeHelpModal}>Close</Button> + </Modal.Footer> + </Modal> + </div> </div> ); } diff --git a/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBarConstants.js b/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBarConstants.js index 975584a..a836275 100644 --- a/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBarConstants.js +++ b/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBarConstants.js @@ -35,3 +35,4 @@ export const SEARCH_DEBOUNCE_TIME = 300; export const ICON_CLASS_SEARCH = 'fa fa-search fa-lg'; export const ICON_CLASS_CLEAR = 'fa fa-times fa-lg'; +export const ICON_CLASS_HELP = 'fa fa-question-circle fa-lg'; diff --git a/src/generic-components/filter/Filter.jsx b/src/generic-components/filter/Filter.jsx new file mode 100644 index 0000000..2270665 --- /dev/null +++ b/src/generic-components/filter/Filter.jsx @@ -0,0 +1,270 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React, {Component} from 'react'; +import Col from 'react-bootstrap/lib/Col'; +import Row from 'react-bootstrap/lib/Row'; +import Grid from 'react-bootstrap/lib/Grid'; +import AddFilters from './components/AddFilters.jsx'; +import ClearFilter from './components/ClearFilter.jsx'; +import RunFilterQuery from './components/RunFilterQuery.jsx'; +import SelectFilter from './components/SelectFilter.jsx'; +import FilterTypes from './components/FilterTypes.jsx'; +import {GlobalExtConstants} from 'utils/GlobalExtConstants.js'; +let APERTURE_SERVICE = JSON.parse(sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'APERTURE_SERVICE')); + +class Filter extends Component { + constructor(props){ + super(props); + APERTURE_SERVICE=JSON.parse(sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'APERTURE_SERVICE')); + }; + state = { + filterList: this.props.filterList, + filterSelected: this.props.nodeType, + filterDisplay: 'Select Filter' , + filterTypeDisplay: 'Filter Type', + errorMsg: '', + showFilter:(this.props.filterMessage && this.props.filterMessage.length > 0) ? true : false, + filterMessage:this.props.filterMessage, + filterValue:'', + filterSelectedList:this.props.filterSelectedList, + isRunEnable:this.props.isRunEnable, + enableRealTime:JSON.parse(sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'ENABLE_ANALYSIS')) + }; + componentDidMount(){ + console.log('Filter component mount'); + }; + componentWillReceiveProps(){ + console.log('filter component componentWillReceiveProps'); + this.setState({ + filterDisplay:this.props.filterDisplay, + filterTypeDisplay:this.props.filterTypeDisplay, + errorMsg:(this.props.errorMsg) ? this.props.errorMsg : '', + filterSelected:this.props.nodeType, + showFilter:(this.props.filterMessage && this.props.filterMessage.length > 0) ? true : false, + filterMessage: this.props.filterMessage, + filterValue:'', + filterSelectedList:this.props.filterSelectedList, + enableRealTime:JSON.parse(sessionStorage.getItem(GlobalExtConstants.ENVIRONMENT + 'ENABLE_ANALYSIS')) + }); + } + renderFilterMessage = (props) => { + console.log('Filter render Filter Message>>>>>',props.showFilter); + if(props.showFilter){ + const filters = props.filterSelectedList.map( (filterMsg,index) =>{ + let filterType=(this.state.enableRealTime && filterMsg.type ==='=')? 'CONTAINS' : filterMsg.type; + return ( + <div className = 'badgeFilter' key={index}> + <span><b>{filterMsg.id}</b> <i>{filterType}</i> {filterMsg.value}</span> + <button type='button' className='close' aria-label='Close' onClick={() => this.onRemoveFilters(filterMsg.id.trim(),filterMsg.value.trim(),filterMsg.type)}> + <span aria-hidden='true'>×</span> + </button> + </div> + ); + }); + console.log('render Filter Message>>>>>before return '); + return( + <Row className='show-grid topBottomMargin'> + <Col md={12} className='removeLeftPadding'> + { filters } + </Col> + </Row> + ); + console.log('Filter render Filter Message>>>>>After return '); + } + }; + renderError = (props) => { + if(props.errorMsg) { + return( + <Row className='show-grid topBottomMargin'> + <span className='label badge-pill label-danger topBottomMargin'><strong>Error </strong>: {this.state.errorMsg}</span> + </Row> + ); + } + }; + filterClearAllButtonSelectedHandler = () => { + console.log('clear all called'); + if(this.state.isRunEnable || this.state.filterMessage.length === 0){ + this.setState( + { + filterDisplay: 'Select Filter', + filterTypeDisplay: 'Filter Type', + errorMsg: '', + filterMessage: [], + filterValue:'', + filterSelectedList:[] + } + ); + }else{ + var tempState = this.state; + tempState.filterMessage = []; + tempState.filterSelectedList = []; + tempState.filterValue = ''; + tempState.errorMsg = ''; + tempState.filterDisplay = 'Select Filter'; + tempState.filterTypeDisplay = 'Filter Type'; + this.setState(tempState,function(){this.props.loadInventory(tempState);}); + } + + }; + filterAddButtonSelectedHandler = () => { + console.log('add Filter called'); + var found = this.isContaining(this.state.filterDisplay, this.state.filterSelectedList); + console.log('filterAddButtonSelectedHandler>>>>>found',found); + this.errorMsg = null; + let filterDisplayState = true; + if(this.state.enableRealTime){ + if(this.state.filterTypeDisplay !== 'Filter Type'){ + filterDisplayState = true; + }else{ + filterDisplayState = false; + } + } + if (this.state.filterDisplay !== 'Select Filter' && filterDisplayState && this.state.filterValue && !found){ + console.log('filterAddButtonSelectedHandler>>>>>inside',this.state.filterValue); + var tempState = this.state; + if(this.state.enableRealTime){ + tempState.filterMessage.push(this.state.filterDisplay + this.state.filterTypeDisplay +this.state.filterValue); + tempState.filterSelectedList.push({'id' : this.state.filterDisplay, 'value' : this.state.filterValue,'type': this.state.filterTypeDisplay}); + }else{ + tempState.filterMessage.push(this.state.filterDisplay + '=' +this.state.filterValue); + tempState.filterSelectedList.push({'id' : this.state.filterDisplay, 'value' : this.state.filterValue,'type': '='}); + } + tempState.filterDisplay = 'Select Filter'; + tempState.filterTypeDisplay = 'Filter Type'; + tempState.filterValue = ''; + tempState.showFilter = true; + tempState.errorMsg = ''; + console.log('filterAddButtonSelectedHandler>>>>>tempState',tempState); + if(this.state.isRunEnable) { + this.setState(tempState); + }else{ + this.setState(tempState,function(){this.props.loadInventory(tempState);}); + } + }else{ + console.log('filterAddButtonSelectedHandler>>>>>Else',this.state.filterDisplay); + console.log('filterAddButtonSelectedHandlerfilterTypeDisplay>>>>>Else',this.state.filterTypeDisplay); + console.log('this.state.filterValue>>>>>>>>>>>>>>',this.state.filterValue); + if(found){ + this.setState({errorMsg: 'Please remove the current filter for this field before trying to add another.'}); + }else if ( this.state.filterDisplay === 'Select Filter'){ + this.setState({errorMsg: 'Please select a filter.'}); + }else if (this.state.enableRealTime && this.state.filterTypeDisplay === 'Filter Type'){ + this.setState({errorMsg: 'Please select a filter type.'}); + }else{ + this.setState({errorMsg: 'Please validate your filter, there seems to be an issue.'}); + } + } + }; + isContaining(nameKey, listArray){ + var found = false; + if(listArray) { + listArray.map((lists) => { + if(lists.id === nameKey){ + console.log('foundName key in list',lists.id); + found = true; + } + }); + } + return found; + }; + onTargetMenuSelect = (listName) => { + console.log('onTargetMenuSelect',listName); + this.setState({filterDisplay:listName,errorMsg:''}); + }; + onTargetMenuOfFilterTypes = (listName) => { + console.log('onTargetMenuOfFilterTypes',listName); + this.setState({filterTypeDisplay:listName,errorMsg:''}); + } + onInputDataChange = (event) =>{ + console.log('inputtext',event.target.value); + this.setState({filterValue:event.target.value}); + }; + onRemoveFilters = (filter,filterText,filterType) => { + console.log('onRemoveFilters',this.state.filterSelectedList); + var found = this.isContaining(filter, this.state.filterSelectedList); + console.log('onRemoveFilters.....found',found); + if(found){ + const filterList = this.state.filterSelectedList.filter(function(el) { + console.log('el.id',el.id); + return el.id !== filter; + }); + console.log('onRemoveFilters.....filterList',filterList); + let message = filter + filterType + filterText; + const filterMsgList = this.state.filterMessage.filter((el) =>{ + return el !== message; + }); + console.log('onRemoveFilters.....filterMsgList',filterMsgList); + if(this.state.isRunEnable) { + this.setState({filterSelectedList:filterList,filterValue:'',filterMessage:filterMsgList,errorMsg:''}); + }else{ + var tempState = this.state; + tempState.filterMessage = filterMsgList; + tempState.filterSelectedList = filterList; + tempState.filterValue = ''; + tempState.errorMsg = ''; + this.setState(tempState,function(){this.props.loadInventory(tempState);}); + } + } + }; + render(){ + let filterTags =''; + if(APERTURE_SERVICE && this.state.enableRealTime){ + console.log('before passing Filter>*',this.state); + filterTags= <Col md={(this.state.isRunEnable) ? 2 : 2} className='removeLeftPadding'> + <FilterTypes param={this.state} + filterList={this.props.filterList} + onMenuSelect={this.onTargetMenuOfFilterTypes} /> + </Col> + } + return( + <div id='filterPane' className={this.props.isFilterEnable ? 'show' : 'hidden'}> + <Grid className='custom-container'> + <Row className='show-grid topBottomMargin'> + <Col md={(this.state.isRunEnable) ? 3 : 2} className='removeLeftPadding'> + <SelectFilter param={this.state} + filterList={this.props.filterList} + onMenuSelect={this.onTargetMenuSelect} /> + </Col> + {filterTags} + <Col md={(this.state.isRunEnable) ? 7 : 8}> + <input type='text' size='36' + onBlur={(event) => this.onInputDataChange(event)} + placeholder='Please Enter Filter text' + value={this.state.filterValue} + onChange={(event) => this.onInputDataChange(event)} /> + <AddFilters param={this.state} + addHandler={this.filterAddButtonSelectedHandler} /> + <ClearFilter param={this.state} clearAllHandler={this.filterClearAllButtonSelectedHandler} /> + <RunFilterQuery param={this.state} /> + </Col> + </Row> + { + this.renderError(this.state) + } + { + this.renderFilterMessage(this.state) + } + </Grid> + </div> + ); + } +} +export default Filter; diff --git a/src/generic-components/filter/components/AddFilters.jsx b/src/generic-components/filter/components/AddFilters.jsx new file mode 100644 index 0000000..4b0aa7b --- /dev/null +++ b/src/generic-components/filter/components/AddFilters.jsx @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React from 'react'; + +const AddFilters = (props) => ( + <button type='button' className='btn btn-primary' onClick={props.addHandler}>Add Filter</button> +); + +export default AddFilters; diff --git a/src/generic-components/filter/components/ClearFilter.jsx b/src/generic-components/filter/components/ClearFilter.jsx new file mode 100644 index 0000000..e231ab8 --- /dev/null +++ b/src/generic-components/filter/components/ClearFilter.jsx @@ -0,0 +1,27 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React from 'react'; + +const ClearFilter = (props) => ( + <button type='button' className='btn btn-secondary' onClick={props.clearAllHandler}>Clear Filters</button> +); + +export default ClearFilter; diff --git a/src/generic-components/filter/components/FilterTypes.jsx b/src/generic-components/filter/components/FilterTypes.jsx new file mode 100644 index 0000000..c25749d --- /dev/null +++ b/src/generic-components/filter/components/FilterTypes.jsx @@ -0,0 +1,102 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React, {Component} from 'react'; +import DropdownButton from 'react-bootstrap/lib/DropdownButton'; +import Dropdown from 'react-bootstrap/lib/Dropdown'; +import MenuItem from 'react-bootstrap/lib/MenuItem'; +import {GlobalExtConstants} from 'utils/GlobalExtConstants.js'; +var filterTypeList = GlobalExtConstants.FILTER_TYPES; + +class FilterTypes extends Component { + constructor(props) { + console.log('FilterTypes props>>>>',props); + super(props); + this.props = props; + this.state = {dropdownIsOpen : false, isInitialize: true} + } + componentWillReceiveProps (nextProps) { + console.log('next props componentWillReceiveProps>>>>>>>',nextProps); + console.log('tihs props componentWillReceiveProps>>>>>>>',this.props); + if(this.state.isInitialize || this.props !== nextProps){ + this.props=nextProps; + this.setState({isInitialize:false}, ()=>{this.updateDropDownState();}); + } + console.log('this.state under Update>>>>>',this.state); + } + handleDropdownValues = (props) => { + const listItems = Object.keys(filterTypeList).map((filter,index) => { + return( + <MenuItem + eventKey={index} + key={filter}> + {filterTypeList[index]} + </MenuItem> + ); + }); + return ( + listItems + ); + }; + toggleDropdown = () => { + console.log('toggleDropdown>>>>>',this.state.dropdownIsOpen); + this.setState({ dropdownIsOpen: !this.state.dropdownIsOpen },()=>{this.updateDropDownState()}); + }; + updateDropDownState = () =>{ + console.log('updateDropDownState',this.state.dropdownIsOpen); + //document.dispatchEvent(new MouseEvent('click')); + let id=(this.props.id)? 'dropdown-root-'+this.props.id :'dropdown-root-2' + if(this.state.dropdownIsOpen){ + document.getElementById(id).getElementsByClassName('dropdown-menu')[0].style.display='block'; + }else{ + document.getElementById(id).getElementsByClassName('dropdown-menu')[0].style.display='none'; + } + } + handleSelect(eventKey, event) { + Object.keys(filterTypeList).map((filter,index) => { + if(eventKey === index){ + this.props.onMenuSelect(filterTypeList[index],this.props.id) + } + }); + } + render(){ + if(this.state.isInitialize){ + this.setState({isInitialize:false},()=>{this.updateDropDownState();}); + } + return( + <div id={(this.props.id)? 'dropdown-root-'+this.props.id :'dropdown-root-2'}> + <DropdownButton + bsStyle='primary' + title= {(this.props.selectedFilter)? this.props.selectedFilter: this.props.param.filterTypeDisplay} + key= '2' + id={(this.props.id)? 'dropdown-basic-'+this.props.id :'dropdown-basic-2'} + className='dropdownButton' + onToggle={this.toggleDropdown} + disabled={(this.props.state)?this.props.state:false} + onSelect={this.handleSelect.bind(this)} > + { + this.handleDropdownValues(this.props) + } + </DropdownButton> + </div> + ) + } +} +export default FilterTypes; diff --git a/src/generic-components/filter/components/RunFilterQuery.jsx b/src/generic-components/filter/components/RunFilterQuery.jsx new file mode 100644 index 0000000..5b81769 --- /dev/null +++ b/src/generic-components/filter/components/RunFilterQuery.jsx @@ -0,0 +1,56 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React from 'react'; +import {Link} from 'react-router-dom'; +//import {BrowserRouter} from 'react-router-dom'; +import {GlobalExtConstants} from 'utils/GlobalExtConstants.js'; +let URI_DELIMITCHAR = GlobalExtConstants.URI_DELIMITCHAR; + +var prepareURI = (props) => { + console.log('prepare URI'); + let URI = '/model/' + props.filterSelected; + let filterList = props.filterSelectedList; + if(filterList){ + for (var key in filterList){ + if(filterList.hasOwnProperty(key)){ + URI += ';' + filterList[key].id + URI_DELIMITCHAR + filterList[key].type + URI_DELIMITCHAR + filterList[key].value; + } + } + } + return ( + URI + ); +}; +const RunFilterQuery = (props) => { + if(props.param.isRunEnable){ + return( + <Link to={prepareURI(props.param)}> + <button type='button' className='btn btn-warning'>Run </button> + </Link> + ); + }else{ + return( + <span></span> + ); + } +}; + +export default RunFilterQuery; diff --git a/src/generic-components/filter/components/SelectFilter.jsx b/src/generic-components/filter/components/SelectFilter.jsx new file mode 100644 index 0000000..94f1807 --- /dev/null +++ b/src/generic-components/filter/components/SelectFilter.jsx @@ -0,0 +1,100 @@ +/* + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2017-2021 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 React, {Component} from 'react'; +import DropdownButton from 'react-bootstrap/lib/DropdownButton'; +import MenuItem from 'react-bootstrap/lib/MenuItem'; + +class SelectFilter extends Component { + constructor(props) { + console.log('SelectFilter props',props); + super(props); + this.props = props; + this.state = {dropdownIsOpen : false, isInitialize: true} + } + componentWillReceiveProps (nextProps) { + console.log('next props componentWillReceiveProps>>>>>>>',nextProps); + console.log('tihs props componentWillReceiveProps>>>>>>>',this.props); + if(this.state.isInitialize || this.props.id !== nextProps.id){ + this.props=nextProps; + this.setState({isInitialize:false},()=>{this.updateDropDownState();}); + } + console.log('this.state under Update>>>>>',this.state); + } + + handleDropdownValues = (props) => { + const listItems = Object.keys(props.filterList).map((filter,index) => { + let filterValue=(props.filterList[index].value)?props.filterList[index].value:props.filterList[index]; + let description=(props.filterList[index].description)?props.filterList[index].description:''; + return( + <MenuItem eventKey={index} key={index} title={description}>{filterValue}</MenuItem> + ); + }); + console.log('listItems',listItems); + return ( + listItems + ); + }; + toggleDropdown = () => { + console.log('toggleDropdown>>>>>',this.state.dropdownIsOpen); + this.setState({ dropdownIsOpen: !this.state.dropdownIsOpen },()=>{this.updateDropDownState();}); + }; + updateDropDownState = () =>{ + console.log('updateDropDownState',this.state.dropdownIsOpen); + //document.dispatchEvent(new MouseEvent('click'));dropdownIsOpen + let id=(this.props.id)? 'dropdown-root-'+this.props.id :'dropdown-root-1' + if(this.state.dropdownIsOpen){ + document.getElementById(id).getElementsByClassName('dropdown-menu')[0].style.display='block'; + }else{ + document.getElementById(id).getElementsByClassName('dropdown-menu')[0].style.display='none'; + } + } + handleSelect(eventKey, event) { + Object.keys(this.props.filterList).map((filter,index) => { + if(eventKey === index){ + let filterValue=(this.props.filterList[index].value)?this.props.filterList[index].value:this.props.filterList[index]; + this.props.onMenuSelect(filterValue,this.props.id) + } + }); + } + render(){ + if(this.state.isInitialize){ + this.setState({isInitialize:false},()=>{this.updateDropDownState();}); + } + return( + <div id={(this.props.id)? 'dropdown-root-'+this.props.id :'dropdown-root-1'}> + <DropdownButton + bsStyle='primary' + title= {(this.props.selectedFilter)? this.props.selectedFilter: this.props.param.filterDisplay} + key= '1' + id={(this.props.id)? 'dropdown-basic-'+this.props.id :'dropdown-basic-1'} + className='dropdownButton' + onToggle={this.toggleDropdown} + onSelect={this.handleSelect.bind(this)} + > + { + this.handleDropdownValues(this.props) + } + </DropdownButton> + </div> + ); + } +} +export default SelectFilter; |