summaryrefslogtreecommitdiffstats
path: root/src/generic-components
diff options
context:
space:
mode:
authorShawn Severin <shawn.severin@amdocs.com>2017-12-07 15:19:25 -0500
committerShawn Severin <shawn.severin@amdocs.com>2017-12-07 15:19:46 -0500
commita7b4b96afee33a2ff458f906742d88cd306ed961 (patch)
treedcf2b16fdc4f52368327d372112cebaf126a1ded /src/generic-components
parent6ac2e7c0bef5ee478b36181b8bb384119ad3d937 (diff)
Adding filter bar
Issue-ID: AAI-543 Change-Id: I18ec69f4585a9f01feafd009fcd30493a039b064 Signed-off-by: Shawn Severin <shawn.severin@amdocs.com>
Diffstat (limited to 'src/generic-components')
-rw-r--r--src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx21
-rw-r--r--src/generic-components/dynamicViewLoader/DynamicViewLoaderReducer.js21
-rw-r--r--src/generic-components/filterBar/FilterBarConstants.js58
-rw-r--r--src/generic-components/filterBar/FilterBarUtils.js352
-rw-r--r--src/generic-components/graph/ForceDirectedGraph.jsx262
-rw-r--r--src/generic-components/graph/NodeFactory.js46
-rw-r--r--src/generic-components/graph/NodeVisualElementConstants.js22
-rw-r--r--src/generic-components/graph/TempCreateAttributes.js464
-rw-r--r--src/generic-components/treeNode/TreeNode.jsx79
9 files changed, 1187 insertions, 138 deletions
diff --git a/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx b/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx
index 455a3ab..bd71e66 100644
--- a/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx
+++ b/src/generic-components/autoCompleteSearchBar/AutoCompleteSearchBar.jsx
@@ -1,25 +1,28 @@
/*
- * ============LICENSE_START=======================================================
- * org.onap.aai
- * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
* Copyright © 2017 Amdocs
- * ================================================================================
+ * All rights reserved.
+ * ============================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=====================================================
*
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
*/
+
import React, {Component} from 'react';
import {Button} from 'react-bootstrap';
import AutoSuggest from 'react-autosuggest';
@@ -120,6 +123,7 @@ export default class AutoCompleteSearchBar extends Component {
onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={(event, {suggestion}) => {
this.newSearchSelected(suggestion, onInvalidSearch, dispatchAnalytics, value);
+ this.props.onClearSuggestionsTextFieldRequested();
}}
renderSuggestion={this.renderSuggestion}
inputProps={inputProps}
@@ -134,6 +138,7 @@ export default class AutoCompleteSearchBar extends Component {
<Button type='submit' className='auto-complete-search-button' onClick={() => {
this.newSearchSelected(this.getSelectedSuggestionObj(value, cachedSuggestions),
onInvalidSearch, dispatchAnalytics, value);
+ this.props.onSuggestionsClearRequested();
}}>
<i className={ICON_CLASS_SEARCH} aria-hidden='true'/>
</Button>
diff --git a/src/generic-components/dynamicViewLoader/DynamicViewLoaderReducer.js b/src/generic-components/dynamicViewLoader/DynamicViewLoaderReducer.js
index c117fc5..969f645 100644
--- a/src/generic-components/dynamicViewLoader/DynamicViewLoaderReducer.js
+++ b/src/generic-components/dynamicViewLoader/DynamicViewLoaderReducer.js
@@ -1,32 +1,33 @@
/*
- * ============LICENSE_START=======================================================
- * org.onap.aai
- * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
* Copyright © 2017 Amdocs
- * ================================================================================
+ * All rights reserved.
+ * ============================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=====================================================
*
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
*/
+
import {combineReducers} from 'redux';
-import DateRangeSelectorReducer from 'generic-components/dateRangeSelector/DateRangeSelectorReducer.js';
import {dynamicViewLoaderActionTypes} from 'generic-components/dynamicViewLoader/DynamicViewLoaderConstants.js';
export default combineReducers({
- dateRangeSelectorData: DateRangeSelectorReducer,
dynamicViewLoadData: (state = {}, action) => {
switch (action.type) {
case dynamicViewLoaderActionTypes.DVL_LAYOUT_SOURCE_CHANGE:
diff --git a/src/generic-components/filterBar/FilterBarConstants.js b/src/generic-components/filterBar/FilterBarConstants.js
new file mode 100644
index 0000000..4226774
--- /dev/null
+++ b/src/generic-components/filterBar/FilterBarConstants.js
@@ -0,0 +1,58 @@
+/*
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * 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=====================================================
+ *
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
+ */
+
+import keyMirror from 'utils/KeyMirror.js';
+import {BASE_URL} from 'app/networking/NetworkConstants.js';
+
+export const filterBarActionTypes = keyMirror({
+ SET_FILTERS: null,
+ SET_FILTER_VALUES: null,
+ CLEAR_FILTERS: null,
+ FILTER_VALUE_CHANGE: null,
+ NEW_SELECTIONS: null,
+ SET_NON_CONVERTED_VALUES: null,
+ SET_CONVERTED_VALUES: null,
+ SET_UNIFIED_VALUES: null
+});
+
+export const UNIFIED_FILTERS_URL = BASE_URL + '/rest/search/unifiedFilterRequest';
+
+export const DISCOVER_FILTERS_ERROR_MSG = 'There was an error retrieving the' +
+ ' list of available filters';
+
+export const FILTER_BAR_TITLE = 'FILTER BY';
+export const DATE_TIME_ZONE = 'Z';
+
+export const FILTER_TYPE_ENUM = {
+ LIST: 'dropDown',
+ DATE: 'date'
+};
+
+export const FILTER_ATTRIBUTE_DEFAULT_VALUE = 'defaultValue';
+export const FILTER_ATTRIBUTE_CONTROLS = 'controls';
+export const FILTER_ATTRIBUTE_CODE = 'code';
+export const FILTER_ATTRIBUTE_VALUES = 'values';
+export const FILTER_ATTRIBUTE_TO = 'to';
+export const FILTER_ATTRIBUTE_FROM = 'from';
diff --git a/src/generic-components/filterBar/FilterBarUtils.js b/src/generic-components/filterBar/FilterBarUtils.js
new file mode 100644
index 0000000..982bda9
--- /dev/null
+++ b/src/generic-components/filterBar/FilterBarUtils.js
@@ -0,0 +1,352 @@
+/*
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * 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=====================================================
+ *
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
+ */
+import isEmpty from 'lodash/isEmpty';
+import moment from 'moment-timezone';
+import {
+ POST,
+ POST_HEADER
+} from 'app/networking/NetworkConstants.js';
+import {
+ UNIFIED_FILTERS_URL,
+ DISCOVER_FILTERS_ERROR_MSG,
+ DATE_TIME_ZONE,
+ FILTER_ATTRIBUTE_TO,
+ FILTER_ATTRIBUTE_FROM,
+ FILTER_ATTRIBUTE_CODE,
+ FILTER_ATTRIBUTE_VALUES,
+ FILTER_ATTRIBUTE_CONTROLS,
+ FILTER_ATTRIBUTE_DEFAULT_VALUE,
+ FILTER_TYPE_ENUM,
+ filterBarActionTypes
+} from 'generic-components/filterBar/FilterBarConstants.js';
+import {
+ MESSAGE_LEVEL_WARNING
+} from 'utils/GlobalConstants.js';
+import {
+ getSetGlobalMessageEvent
+} from 'app/globalInlineMessageBar/GlobalInlineMessageBarActions.js';
+
+export function buildFilterValueMap(filterValueString) {
+ let filterValueObj = {};
+ let filters = filterValueString.split(',');
+
+ for (let filterIndex in filters) {
+ let filterStringParts = filters[filterIndex].split('=');
+
+ filterValueObj[filterStringParts[0]] = filterStringParts[1];
+ }
+
+ return filterValueObj;
+}
+
+export function buildFilterValueMapFromObj(filterValues) {
+ let filterValueObj = {};
+
+ for (let filterIndex in filterValues) {
+ filterValueObj[filterValues[filterIndex].filterId] = filterValues[filterIndex].filterValue;
+ }
+
+ return filterValueObj;
+}
+
+export function getFilterListQueryString(filterValueList) {
+ let filterQueryList = [];
+
+ for (let filter in filterValueList) {
+ if (filterValueList[filter]) {
+ filterQueryList.push(
+ {
+ 'filterId': filter,
+ 'filterValue': filterValueList[filter]
+ }
+ );
+ }
+ }
+
+ return filterQueryList;
+}
+
+function getFilterSearchURL() {
+ return UNIFIED_FILTERS_URL.replace('@@IP-ADDRESS@@', document.location.hostname);
+}
+
+function getFiltersQueryObject(viewName) {
+ return {
+ 'viewName': viewName
+ };
+}
+
+function getFiltersEvent(actionType, filterList) {
+ return {
+ type: actionType,
+ data: filterList
+ };
+}
+
+export function getUnifiedFilters(viewName, actionType) {
+ return dispatch => {
+ return fetch(getFilterSearchURL(), {
+ credentials: 'same-origin',
+ method: POST,
+ headers: POST_HEADER,
+ body: JSON.stringify(getFiltersQueryObject(viewName))
+ }).then(
+ (response) => response.json()
+ ).then(
+ (responseJson) => {
+ dispatch(
+ getFiltersEvent(actionType, responseJson.filters)
+ );
+ }
+ ).catch(
+ () => {
+ dispatch(getSetGlobalMessageEvent(DISCOVER_FILTERS_ERROR_MSG, MESSAGE_LEVEL_WARNING));
+ }
+ );
+ };
+}
+
+function extractConvertedDateValues(dateValues) {
+ let convertedValues = {};
+ if (dateValues.from) {
+ let startMoment = moment(dateValues.from);
+ convertedValues.startDate = startMoment.toDate();
+ convertedValues.time_zone = startMoment.format(DATE_TIME_ZONE);
+ }
+
+ if (dateValues.to) {
+ let endMoment = moment(dateValues.to);
+ convertedValues.endDate = endMoment.toDate();
+ convertedValues.time_zone = endMoment.format(DATE_TIME_ZONE);
+ }
+
+ convertedValues.code = dateValues.code;
+
+ return convertedValues;
+}
+
+function convertFilterValues(filterValues) {
+ let convertedFilterValues = {};
+
+ for (let filterId in filterValues) {
+ if (filterValues.hasOwnProperty(filterId) &&
+ !isEmpty(filterValues[filterId]) && !isEmpty(filterValues[filterId].controls)) {
+ let controls = filterValues[filterId].controls;
+ let firstControlKey = Object.keys(controls)[0];
+ if (controls[firstControlKey][FILTER_ATTRIBUTE_VALUES][FILTER_ATTRIBUTE_FROM] ||
+ controls[firstControlKey][FILTER_ATTRIBUTE_VALUES][FILTER_ATTRIBUTE_TO]) {
+ // TODO should check against filter type (ex: dropdown or date)
+ // rather than assuming value attributes (ex: 'to' or 'from')
+ convertedFilterValues[filterId] =
+ extractConvertedDateValues(controls[firstControlKey][FILTER_ATTRIBUTE_VALUES]);
+ } else {
+ let codeValue = controls[firstControlKey][FILTER_ATTRIBUTE_VALUES][FILTER_ATTRIBUTE_CODE];
+ convertedFilterValues[filterId] = codeValue;
+ }
+ }
+ }
+
+ return convertedFilterValues;
+}
+
+function combineMissingFilters(filterValues, allFilters) {
+ let allFilterIds = Object.keys(allFilters);
+
+ for (let id in allFilterIds) {
+ if (!filterValues.hasOwnProperty(allFilterIds[id])) {
+ filterValues[allFilterIds[id]] = '';
+ }
+ }
+
+ return filterValues;
+}
+
+function getFilterSelectionEvent(selectedValuesMap, convertedValues) {
+ return {
+ type: filterBarActionTypes.NEW_SELECTIONS,
+ data: {
+ selectedValuesMap: convertedValues,
+ unifiedValues: selectedValuesMap
+ }
+ };
+}
+
+export function processFilterSelection(filterValues, allFilters) {
+ let convertedFilterValues = convertFilterValues(filterValues);
+ let combinedFilterValues = combineMissingFilters(convertedFilterValues, allFilters);
+
+ // dispatch NEW_SELECTION event type with converted values as the data
+ return getFilterSelectionEvent(filterValues, combinedFilterValues);
+}
+
+export function setNonConvertedFilterValues(nonConvertedValues) {
+ return {
+ type: filterBarActionTypes.SET_NON_CONVERTED_VALUES,
+ data: nonConvertedValues
+ };
+}
+
+function convertedFilterValuesEvent(nonConvertedValues, convertedValues) {
+ return {
+ type: filterBarActionTypes.SET_CONVERTED_VALUES,
+ data: {
+ nonConvertedValues: nonConvertedValues,
+ convertedValues: convertedValues
+ }
+ };
+}
+
+function mapValuesToOption(filterOptions, nonConvertedValue) {
+ let mappedValues = {};
+
+ // loop over options to find match for value
+ for (let i in filterOptions) {
+ if (filterOptions[i].code === nonConvertedValue) {
+ // found the matching
+ mappedValues = filterOptions[i];
+ break;
+ }
+ }
+
+ return mappedValues;
+}
+
+function mapValuesToDateOption(nonConvertedValue) {
+ let mappedValues = {};
+
+ if (nonConvertedValue.startDate) {
+ mappedValues.from = new Date(nonConvertedValue.startDate);
+ } else {
+ mappedValues.from = null;
+ }
+
+ if (nonConvertedValue.endDate) {
+ mappedValues.to = new Date(nonConvertedValue.endDate);
+ } else {
+ mappedValues.to = null;
+ }
+
+ mappedValues.code = nonConvertedValue.code;
+
+ return mappedValues;
+}
+
+function mapValuesToFilter(nonConvertedValues, allFilters, currentlySetFilterValues) {
+ let convertedValues = {};
+
+ for (let nonConvertedId in nonConvertedValues) {
+ if (nonConvertedValues[nonConvertedId] !== '') {
+ let matchingFilterObj = allFilters[nonConvertedId];
+ let filterControlId = Object.keys(matchingFilterObj.controls)[0];
+ let mappedValue = {};
+
+ if (matchingFilterObj[FILTER_ATTRIBUTE_CONTROLS][filterControlId].type === FILTER_TYPE_ENUM.DATE) {
+ mappedValue = mapValuesToDateOption(nonConvertedValues[nonConvertedId]);
+ } else {
+ mappedValue = mapValuesToOption(matchingFilterObj[FILTER_ATTRIBUTE_CONTROLS][filterControlId].options,
+ nonConvertedValues[nonConvertedId]);
+ }
+
+ let values = {};
+ values[FILTER_ATTRIBUTE_VALUES] = mappedValue;
+ let filterControlers = {};
+ filterControlers[filterControlId] = values;
+ let filter = {};
+ filter[FILTER_ATTRIBUTE_CONTROLS] = filterControlers;
+ convertedValues[nonConvertedId] = filter;
+ } else if (!isEmpty(currentlySetFilterValues[nonConvertedId])) {
+ // currently a value is set for this filter, need to ensure we map this filter
+ // to an empty value so that it is cleared/reset
+ let matchingFilterObj = allFilters[nonConvertedId];
+ let filterControlId = Object.keys(matchingFilterObj.controls)[0];
+ let mappedValue = {};
+ let values = {};
+ values[FILTER_ATTRIBUTE_VALUES] = mappedValue;
+ let filterControlers = {};
+ filterControlers[filterControlId] = values;
+ let filter = {};
+ filter[FILTER_ATTRIBUTE_CONTROLS] = filterControlers;
+ convertedValues[nonConvertedId] = filter;
+ }
+ }
+
+ return convertedValues;
+}
+
+export function convertNonConvertedValues(nonConvertedValues, allFilters, currentlySetFilterValues) {
+ let convertedValues = mapValuesToFilter(nonConvertedValues, allFilters, currentlySetFilterValues);
+ return convertedFilterValuesEvent(nonConvertedValues, convertedValues);
+}
+
+export function clearFilters() {
+ return {
+ type: filterBarActionTypes.CLEAR_FILTERS
+ };
+}
+
+function getSetUnifiedFilterValuesEvent(unifiedValues) {
+ return {
+ type: filterBarActionTypes.SET_UNIFIED_VALUES,
+ data: unifiedValues
+ };
+}
+
+function getFilterDefault(filters, filterId) {
+ let filterControlId = Object.keys(filters[filterId][FILTER_ATTRIBUTE_CONTROLS])[0];
+ let defaultValue = filters[filterId][FILTER_ATTRIBUTE_CONTROLS][filterControlId][FILTER_ATTRIBUTE_DEFAULT_VALUE];
+ if (!defaultValue) {
+ defaultValue = {};
+ }
+ return defaultValue;
+}
+
+export function setFilterSelectionsToDefaults(filters, filterValues) {
+ let defaultFilterMap = {};
+
+ for (let filterId in filters) {
+ let filterDefaultValue = getFilterDefault(filters, filterId);
+ if (!isEmpty(filterDefaultValue) || (filterValues && filterValues[filterId])) {
+ let filterControlId = Object.keys(filters[filterId][FILTER_ATTRIBUTE_CONTROLS])[0];
+ let controller = {};
+ controller.values = filterDefaultValue;
+ let controllers = {};
+ controllers[filterControlId] = controller;
+ let controls = {};
+ controls.controls = controllers;
+ defaultFilterMap[filterId] = controls;
+ }
+ }
+
+ if (isEmpty(defaultFilterMap)) {
+ // there are no default values, so need to ensure all filters get cleared,
+ // but just incase this 'clearing'
+ let combinedValues = combineMissingFilters(defaultFilterMap, filters);
+ return setNonConvertedFilterValues(combinedValues);
+ } else {
+ // jsut set the Unified Filter Value which will be sent down to the filter (filter
+ // will set itself to the default value and then send notification back up of the selection
+ return getSetUnifiedFilterValuesEvent(defaultFilterMap);
+ }
+}
diff --git a/src/generic-components/graph/ForceDirectedGraph.jsx b/src/generic-components/graph/ForceDirectedGraph.jsx
index 872b2d5..00c2575 100644
--- a/src/generic-components/graph/ForceDirectedGraph.jsx
+++ b/src/generic-components/graph/ForceDirectedGraph.jsx
@@ -1,25 +1,28 @@
/*
- * ============LICENSE_START=======================================================
- * org.onap.aai
- * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
* Copyright © 2017 Amdocs
- * ================================================================================
+ * All rights reserved.
+ * ============================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=====================================================
*
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
*/
+
import {drag} from 'd3-drag';
import {forceSimulation, forceLink, forceManyBody, forceCenter} from 'd3-force';
import {interpolateNumber} from 'd3-interpolate';
@@ -44,7 +47,7 @@ class ForceDirectedGraph extends Component {
nodeButtonSelectedCallback: PropTypes.func,
currentlySelectedNodeView: PropTypes.string
};
-
+
static defaultProps = {
viewWidth: 0,
viewHeight: 0,
@@ -57,16 +60,16 @@ class ForceDirectedGraph extends Component {
nodeButtonSelectedCallback: undefined,
currentlySelectedNodeView: ''
};
-
+
constructor(props) {
super(props);
-
+
this.state = {
nodes: [], links: [], mainGroupTransform: zoomIdentity
};
-
+
this.updateSimulationForce = this.updateSimulationForce.bind(this);
- this.resetTransform = this.resetTransform.bind(this);
+ this.resetState = this.resetState.bind(this);
this.applyBufferDataToState = this.applyBufferDataToState.bind(this);
this.createNodePropForState = this.createNodePropForState.bind(this);
this.createLinkPropForState = this.createLinkPropForState.bind(this);
@@ -74,118 +77,138 @@ class ForceDirectedGraph extends Component {
this.simulationComplete = this.simulationComplete.bind(this);
this.simulationTick = this.simulationTick.bind(this);
this.nodeSelected = this.nodeSelected.bind(this);
+ this.nodeButtonSelected = this.nodeButtonSelected.bind(this);
this.onZoom = this.onZoom.bind(this);
this.onGraphDrag = this.onGraphDrag.bind(this);
this.onNodeDrag = this.onNodeDrag.bind(this);
this.addNodeInterpolator = this.addNodeInterpolator.bind(this);
this.runInterpolators = this.runInterpolators.bind(this);
-
+
this.nodeBuffer = [];
this.linkBuffer = [];
this.nodeDatum = [];
this.nodeButtonDatum = [];
this.nodeFactory = new NodeFactory();
this.visualElementFactory = new NodeVisualElementFactory();
-
+
this.isGraphMounted = false;
-
+
this.listenerGraphCounter = -1;
this.nodeIndexTracker = new Map();
this.interpolators = new Map();
this.areInterpolationsRunning = false;
-
+
this.newNodeSelected = true;
this.currentlySelectedNodeButton = undefined;
-
+
this.intervalTimer = interval(this.applyBufferDataToState, simulationKeys.DATA_COPY_INTERVAL);
this.intervalTimer.stop();
-
+
this.interpolationTimer = interval(this.runInterpolators, simulationKeys.DATA_COPY_INTERVAL);
this.interpolationTimer.stop();
-
+
this.simulation = forceSimulation();
this.simulation.on('end', this.simulationComplete);
this.simulation.stop();
-
+
this.svgZoom =
zoom().scaleExtent([NodeConstants.SCALE_EXTENT_MIN, NodeConstants.SACEL_EXTENT_MAX]);
this.svgZoom.clickDistance(2);
this.nodeDrag = drag().clickDistance(2);
-
+
this.updateSimulationForce();
-
+ // Temporary code until backend supports NOT displaying the button in the response.
+ if(props.dataOverlayButtons.length === 1) {
+ this.hideButton = true;
+ } else {
+ this.hideButton = false;
+ }
if (props.graphData) {
if (props.graphData.graphCounter !== -1) {
- this.startSimulation(props.graphData);
+ this.startSimulation(props.graphData, props.currentlySelectedNodeView, props.dataOverlayButtons);
}
}
}
-
+
componentDidMount() {
this.isGraphMounted = true;
}
-
+
componentWillReceiveProps(nextProps) {
if (nextProps.graphData.graphCounter !== this.props.graphData.graphCounter) {
this.listenerGraphCounter = this.props.graphData.graphCounter;
this.newNodeSelected = true;
- this.resetTransform();
- this.startSimulation(nextProps.graphData);
+ this.resetState();
+ this.startSimulation(nextProps.graphData, nextProps.currentlySelectedNodeView, nextProps.dataOverlayButtons);
}
}
-
+
+
componentDidUpdate(prevProps) {
let hasNewGraphDataRendered = (prevProps.graphData.graphCounter ===
this.props.graphData.graphCounter);
let shouldAttachListeners = (this.listenerGraphCounter !== this.props.graphData.graphCounter);
let nodeCount = this.state.nodes.length;
-
+
if (nodeCount > 0) {
if (hasNewGraphDataRendered && shouldAttachListeners) {
-
let nodes = select('.fdgMainSvg').select('.fdgMainG')
.selectAll('.aai-entity-node')
.data(this.nodeDatum);
-
nodes.on('click', (d) => {
this.nodeSelected(d);
});
-
+
nodes.call(this.nodeDrag.on('drag', (d) => {
let xAndY = [currentEvent.x, currentEvent.y];
this.onNodeDrag(d, xAndY);
}));
-
+
let mainSVG = select('.fdgMainSvg');
let mainView = mainSVG.select('.fdgMainView');
this.svgZoom.transform(mainSVG, zoomIdentity);
this.svgZoom.transform(mainView, zoomIdentity);
-
+
mainSVG.call(this.svgZoom.on('zoom', () => { // D3 Zoom also handles panning
this.onZoom(currentEvent.transform);
})).on('dblclick.zoom', null); // Ignore the double-click zoom event
-
+
this.listenerGraphCounter = this.props.graphData.graphCounter;
}
+
+ if (this.newNodeSelected) {
+ let nodeButtons = select('.fdgMainSvg').select('.fdgMainG')
+ .selectAll('.aai-entity-node')
+ .selectAll('.node-button')
+ .data(this.nodeButtonDatum);
+ if (!nodeButtons.empty()) {
+ nodeButtons.on('click', (d) => {
+ this.nodeButtonSelected(d);
+ });
+ if (hasNewGraphDataRendered && shouldAttachListeners) {
+ this.newNodeSelected = false;
+ }
+ }
+ }
}
}
-
+
componentWillUnmount() {
this.isGraphMounted = false;
-
+
let nodes = select('.fdgMainSvg').select('.fdgMainG')
.selectAll('.aai-entity-node');
let nodeButtons = nodes.selectAll('.node-button');
-
+
nodes.on('click', null);
nodeButtons.on('click', null);
-
+
let mainSVG = select('.fdgMainSvg');
-
+
mainSVG.call(this.svgZoom.on('zoom', null)).on('dblclick.zoom', null);
mainSVG.call(drag().on('drag', null));
}
-
+
updateSimulationForce() {
this.simulation.force('link', forceLink());
this.simulation.force('link').id((d) => {
@@ -193,55 +216,65 @@ class ForceDirectedGraph extends Component {
});
this.simulation.force('link').strength(0.3);
this.simulation.force('link').distance(100);
-
+
this.simulation.force('charge', forceManyBody());
this.simulation.force('charge').strength(-1250);
this.simulation.alpha(1);
-
+
this.simulation.force('center',
forceCenter(this.props.viewWidth / 2, this.props.viewHeight / 2));
}
-
- resetTransform() {
+
+ resetState() {
if (this.isGraphMounted) {
this.setState(() => {
return {
- mainGroupTransform: zoomIdentity
+ mainGroupTransform: zoomIdentity,
+ nodes: [], links: []
};
});
}
}
-
+
applyBufferDataToState() {
this.nodeIndexTracker.clear();
-
+
let newNodes = [];
this.nodeBuffer.map((node, i) => {
let nodeProps = this.createNodePropForState(node);
-
- if (nodeProps.meta.nodeMeta.className === NodeConstants.SELECTED_NODE_CLASS_NAME ||
- nodeProps.meta.nodeMeta.className === NodeConstants.SELECTED_SEARCHED_NODE_CLASS_NAME) {
-
+
+ if (nodeProps.meta.nodeMeta.className ===
+ NodeConstants.SELECTED_NODE_CLASS_NAME ||
+ nodeProps.meta.nodeMeta.className ===
+ NodeConstants.SELECTED_SEARCHED_NODE_CLASS_NAME) {
+
this.nodeButtonDatum[0].data = nodeProps.meta;
-
- nodeProps = {
- ...nodeProps,
- buttons: [this.nodeButtonDatum[0].isSelected]
- };
+ if(this.nodeButtonDatum.length > 1) {
+ this.nodeButtonDatum[1].data = nodeProps.meta;
+ nodeProps = {
+ ...nodeProps,
+ buttons: [this.nodeButtonDatum[0].isSelected, this.nodeButtonDatum[1].isSelected]
+ };
+ } else {
+ nodeProps = {
+ ...nodeProps,
+ buttons: [this.nodeButtonDatum[0].isSelected]
+ };
+ }
}
-
- newNodes.push(this.nodeFactory.buildNode(nodeProps.meta.nodeMeta.className, nodeProps));
-
+
+ newNodes.push(this.nodeFactory.buildNode(nodeProps.meta.nodeMeta.className, nodeProps, this.hideButton));
+
this.nodeIndexTracker.set(node.id, i);
});
-
+
let newLinks = [];
this.linkBuffer.map((link) => {
let key = link.id;
let linkProps = this.createLinkPropForState(link);
newLinks.push(this.visualElementFactory.createSvgLine(linkProps, key));
});
-
+
if (this.isGraphMounted) {
this.setState(() => {
return {
@@ -250,7 +283,7 @@ class ForceDirectedGraph extends Component {
});
}
}
-
+
createNodePropForState(nodeData) {
return {
renderProps: {
@@ -260,7 +293,7 @@ class ForceDirectedGraph extends Component {
}
};
}
-
+
createLinkPropForState(linkData) {
return {
className: 'aai-entity-link',
@@ -270,10 +303,10 @@ class ForceDirectedGraph extends Component {
y2: linkData.target.y
};
}
-
- startSimulation(graphData) {
+
+ startSimulation(graphData, currentView, overlayButtons) {
this.nodeFactory.setNodeMeta(graphData.graphMeta);
-
+
// Experiment with removing length = 0... might not be needed as new array
// assignment will likely destroy old reference
this.nodeBuffer.length = 0;
@@ -282,45 +315,60 @@ class ForceDirectedGraph extends Component {
this.linkBuffer = Array.from(graphData.linkDataArray);
this.nodeDatum.length = 0;
this.nodeDatum = Array.from(graphData.nodeDataArray);
-
+
this.nodeButtonDatum.length = 0;
-
- let isNodeDetailsSelected = true;
+
+ let isNodeDetailsSelected = (currentView ===
+ overlayButtons[0] ||
+ currentView ===
+ '');
this.nodeButtonDatum.push({
- name: NodeConstants.ICON_ELLIPSES, isSelected: isNodeDetailsSelected
+ name: NodeConstants.ICON_ELLIPSES, isSelected: isNodeDetailsSelected, overlayName: overlayButtons[0]
});
-
+
+
+ if(overlayButtons.length > 1 ) {
+ let isSecondButtonSelected = (currentView === overlayButtons[1]);
+
+ this.nodeButtonDatum.push({
+ name: NodeConstants.ICON_TRIANGLE_WARNING, isSelected: isSecondButtonSelected, overlayName: overlayButtons[1]
+ });
+ }
+
+
if (isNodeDetailsSelected) {
this.currentlySelectedNodeButton = NodeConstants.ICON_ELLIPSES;
+ } else {
+ this.currentlySelectedNodeButton = NodeConstants.ICON_TRIANGLE_WARNING;
}
-
+
this.updateSimulationForce();
-
+
this.simulation.nodes(this.nodeBuffer);
this.simulation.force('link').links(this.linkBuffer);
this.simulation.on('tick', this.simulationTick);
this.simulation.restart();
}
-
+
simulationComplete() {
this.intervalTimer.stop();
this.applyBufferDataToState();
}
-
+
simulationTick() {
this.intervalTimer.restart(this.applyBufferDataToState, simulationKeys.DATA_COPY_INTERVAL);
this.simulation.on('tick', null);
}
-
+
nodeSelected(datum) {
if (this.props.nodeSelectedCallback) {
this.props.nodeSelectedCallback(datum);
}
-
+
let didUpdateNew = false;
let didUpdatePrevious = false;
let isSameNodeSelected = true;
-
+
// Check to see if a default node was previously selected
let selectedDefaultNode = select('.fdgMainSvg').select('.fdgMainG')
.selectAll('.aai-entity-node')
@@ -333,7 +381,7 @@ class ForceDirectedGraph extends Component {
isSameNodeSelected = false;
}
}
-
+
// Check to see if a searched node was previously selected
let selectedSearchedNode = select('.fdgMainSvg').select('.fdgMainG')
.selectAll('.aai-entity-node')
@@ -346,7 +394,7 @@ class ForceDirectedGraph extends Component {
isSameNodeSelected = false;
}
}
-
+
if (!isSameNodeSelected) {
let newlySelectedNode = select('.fdgMainSvg').select('.fdgMainG')
.selectAll('.aai-entity-node')
@@ -354,7 +402,7 @@ class ForceDirectedGraph extends Component {
return (datum.id === d.id);
});
if (!newlySelectedNode.empty()) {
-
+
if (newlySelectedNode.datum().nodeMeta.searchTarget) {
this.nodeBuffer[newlySelectedNode.datum().index].nodeMeta.className =
NodeConstants.SELECTED_SEARCHED_NODE_CLASS_NAME;
@@ -365,13 +413,35 @@ class ForceDirectedGraph extends Component {
didUpdateNew = true;
}
}
-
+
if (didUpdatePrevious && didUpdateNew) {
this.newNodeSelected = true;
this.applyBufferDataToState();
}
}
+ nodeButtonSelected(datum) {
+ if (this.props.nodeButtonSelectedCallback) {
+ let buttonClickEvent = {
+ buttonId: datum.overlayName
+ };
+ this.props.nodeButtonSelectedCallback(buttonClickEvent);
+ }
+
+ if (this.currentlySelectedNodeButton !== datum.name) {
+ if (datum.name === this.nodeButtonDatum[0].name) {
+ this.nodeButtonDatum[0].isSelected = true;
+ this.nodeButtonDatum[1].isSelected = false;
+ }
+ if (datum.name === this.nodeButtonDatum[1].name) {
+ this.nodeButtonDatum[0].isSelected = false;
+ this.nodeButtonDatum[1].isSelected = true;
+ }
+ this.currentlySelectedNodeButton = datum.name;
+ this.applyBufferDataToState();
+ }
+ }
+
onZoom(eventTransform) {
if (this.isGraphMounted) {
this.setState(() => {
@@ -381,7 +451,7 @@ class ForceDirectedGraph extends Component {
});
}
}
-
+
onGraphDrag(xAndYCoords) {
let translate = `translate(${xAndYCoords.x}, ${xAndYCoords.y})`;
let oldTransform = this.state.mainGroupTransform;
@@ -393,7 +463,7 @@ class ForceDirectedGraph extends Component {
});
}
}
-
+
onNodeDrag(datum, xAndYCoords) {
let nodeIndex = this.nodeIndexTracker.get(datum.id);
if (this.nodeBuffer[nodeIndex]) {
@@ -402,7 +472,7 @@ class ForceDirectedGraph extends Component {
this.applyBufferDataToState();
}
}
-
+
addNodeInterpolator(nodeId, key, startingValue, endingValue, duration) {
let numberInterpolator = interpolateNumber(startingValue, endingValue);
let timeNow = now();
@@ -410,20 +480,20 @@ class ForceDirectedGraph extends Component {
nodeId: nodeId, key: key, duration: duration, timeCreated: timeNow, method: numberInterpolator
};
this.interpolators.set(nodeId, interpolationObject);
-
+
if (!this.areInterpolationsRunning) {
this.interpolationTimer.restart(this.runInterpolators, simulationKeys.DATA_COPY_INTERVAL);
this.areInterpolationsRunning = true;
}
}
-
+
runInterpolators() {
// If we have no more interpolators to run then shut'r down!
if (this.interpolators.size === 0) {
this.interpolationTimer.stop();
this.areInterpolationsRunning = false;
}
-
+
let iterpolatorsComplete = [];
// Apply interpolation values
this.interpolators.forEach((interpolator) => {
@@ -439,22 +509,22 @@ class ForceDirectedGraph extends Component {
this.nodeBuffer[nodeIndex][interpolator.key] = interpolator.method(t);
}
});
-
+
// Remove any interpolators that are complete
if (iterpolatorsComplete.length > 0) {
for (let i = 0; i < iterpolatorsComplete.length; i++) {
this.interpolators.delete(iterpolatorsComplete[i]);
}
}
-
+
this.applyBufferDataToState();
}
-
+
render() {
// We will be using these values veru shortly, commenting out for eslint
// reasons so we can build for PV let {viewWidth, viewHeight} = this.props;
let {nodes, links, mainGroupTransform} = this.state;
-
+
return (
<div className='ts-force-selected-graph'>
<svg className={'fdgMainSvg'} width='100%' height='100%'>
@@ -478,9 +548,9 @@ class ForceDirectedGraph extends Component {
</div>
);
}
-
+
static graphCounter = 0;
-
+
static generateNewProps(nodeArray, linkArray, metaData) {
ForceDirectedGraph.graphCounter += 1;
return {
diff --git a/src/generic-components/graph/NodeFactory.js b/src/generic-components/graph/NodeFactory.js
index da57ffb..abfd73b 100644
--- a/src/generic-components/graph/NodeFactory.js
+++ b/src/generic-components/graph/NodeFactory.js
@@ -1,25 +1,28 @@
/*
- * ============LICENSE_START=======================================================
- * org.onap.aai
- * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
* Copyright © 2017 Amdocs
- * ================================================================================
+ * All rights reserved.
+ * ============================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=====================================================
*
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
*/
+
import React from 'react';
import NodeVisualElementConstants from './NodeVisualElementConstants.js';
@@ -38,11 +41,11 @@ class NodeFactory {
this.graphMeta = metaObject;
this.visualElementFactory.setVisualElementMeta(metaObject);
}
-
- buildNode(nodeType, nodeProps) {
+ // hideButton a temporary solution to not display the button.
+ buildNode(nodeType, nodeProps, hideButton) {
let translate = `translate(
- ${nodeProps.renderProps.x},
+ ${nodeProps.renderProps.x},
${nodeProps.renderProps.y})`;
let finalProps = {
...nodeProps.renderProps,
@@ -51,14 +54,20 @@ class NodeFactory {
};
let nodeVisualElementsData = this.extractVisualElementArrayFromMeta(
- nodeType);
+ nodeType, hideButton);
let nodeVisualElements = undefined;
if (nodeVisualElementsData) {
nodeVisualElements = [];
nodeVisualElementsData.map((elementData, index) => {
if (elementData.type === NodeVisualElementConstants.BUTTON) {
if (nodeProps.buttons) {
- let isButtonSelected = true;
+ let isButtonSelected = false;
+ if (index === 4) {
+ isButtonSelected = nodeProps.buttons[0];
+ }
+ if (index === 5) {
+ isButtonSelected = nodeProps.buttons[1];
+ }
elementData = {
...elementData,
isSelected: isButtonSelected
@@ -99,11 +108,20 @@ class NodeFactory {
return React.createElement('g', finalProps);
}
- extractVisualElementArrayFromMeta(nodeClassName) {
+ extractVisualElementArrayFromMeta(nodeClassName, hideButton) {
let nodeVisualElements = undefined;
if (this.graphMeta.aaiEntityNodeDescriptors) {
nodeVisualElements =
this.graphMeta.aaiEntityNodeDescriptors[nodeClassName].visualElements;
+ if(hideButton) {
+ // temp, until BE not sent the triangle button
+ for (var i = 0; i < nodeVisualElements.length; i++) {
+ if (nodeVisualElements[i].type === 'button' && nodeVisualElements[i].name === 'icon_triangle_warning') {
+ nodeVisualElements.splice(i, 1);
+ return;
+ }
+ }
+ }
}
return nodeVisualElements;
}
diff --git a/src/generic-components/graph/NodeVisualElementConstants.js b/src/generic-components/graph/NodeVisualElementConstants.js
index 11d9f7d..aeeedf7 100644
--- a/src/generic-components/graph/NodeVisualElementConstants.js
+++ b/src/generic-components/graph/NodeVisualElementConstants.js
@@ -1,25 +1,28 @@
/*
- * ============LICENSE_START=======================================================
- * org.onap.aai
- * ================================================================================
- * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
* Copyright © 2017 Amdocs
- * ================================================================================
+ * All rights reserved.
+ * ============================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- * ============LICENSE_END=========================================================
+ * ============LICENSE_END=====================================================
*
- * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
*/
+
export default {
SVG_CIRCLE: 'circle',
SVG_LINE: 'line',
@@ -41,6 +44,5 @@ export default {
ICON_ELLIPSES: 'icon_ellipses',
ICON_TRIANGLE_WARNING: 'icon_triangle_warning',
ICON_TICK: 'icon_tick',
- ICON_WARNING: 'icon_warning',
- BUTTON_CLICK_NODE_DETAILS: 'NODE_DETAILS'
+ ICON_WARNING: 'icon_warning'
};
diff --git a/src/generic-components/graph/TempCreateAttributes.js b/src/generic-components/graph/TempCreateAttributes.js
new file mode 100644
index 0000000..fac32ba
--- /dev/null
+++ b/src/generic-components/graph/TempCreateAttributes.js
@@ -0,0 +1,464 @@
+/*
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * 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=====================================================
+ *
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
+ */
+
+import React from 'react';
+
+
+export default {
+ createNodeAttributes: () => {
+ return {
+ className: 'node'
+ };
+ },
+ createShapeAttributes: () => {
+ return {
+ r: 3
+ };
+ },
+ createCircleStyle: () => {
+ return {
+ fill: 'rgb(98, 102, 104)',
+ stroke: 'rgb(78, 82, 84)',
+ strokeWidth: 1
+ };
+ },
+ createLinkAttributes: () => {
+ return {
+ className: 'link',
+ x1: 50,
+ y1: 50,
+ x2: 100,
+ y2: 500,
+ };
+ },
+ createLineStyle: () => {
+ return {
+ stroke: 'rgb(217, 218, 218)',
+ strokeWidth: 1
+ };
+ },
+ createTestCircle: () => {
+ return React.createElement('circle', {
+ cx: '100',
+ cy: '100',
+ r: '15',
+ fill: 'rgb(255, 255, 255)',
+ stroke: 'rgb(98, 102, 104)',
+ strokeWidth: '3'
+ });
+ },
+ getNodeLinkArray: () => {
+ return {
+ 'graphMeta': {
+ 'aaiEntityNodeDescriptors': {
+ 'generalNodeClass': {
+ 'class': 'aai-entity-node general-node',
+ 'visualElements': [
+ {
+ 'type': 'circle',
+ 'class': 'outer',
+ 'svgAttributes': {
+ 'r': '16'
+ }
+ },
+ {
+ 'type': 'circle',
+ 'class': 'inner',
+ 'svgAttributes': {
+ 'r': '10'
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-type-label',
+ 'displayKey': 'itemType',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '33'
+ }
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-value-label',
+ 'displayKey': 'itemNameValue',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '48'
+ }
+ }
+ }
+ ]
+ },
+ 'searchedNodeClass': {
+ 'class': 'aai-entity-node search-node',
+ 'visualElements': [
+ {
+ 'type': 'circle',
+ 'class': 'outer',
+ 'svgAttributes': {
+ 'r': '16'
+ }
+ },
+ {
+ 'type': 'circle',
+ 'class': 'inner',
+ 'svgAttributes': {
+ 'r': '10'
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-type-label',
+ 'displayKey': 'itemType',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '33'
+ }
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-value-label',
+ 'displayKey': 'itemNameValue',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '48'
+ }
+ }
+ }
+ ]
+ },
+ 'selectedSearchedNodeClass': {
+ 'class': 'aai-entity-node selected-search-node',
+ 'visualElements': [
+ {
+ 'type': 'circle',
+ 'class': 'outer',
+ 'svgAttributes': {
+ 'r': '31'
+ }
+ },
+ {
+ 'type': 'circle',
+ 'class': 'inner',
+ 'svgAttributes': {
+ 'r': '20'
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-type-label',
+ 'displayKey': 'itemType',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '48'
+ }
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-value-label',
+ 'displayKey': 'itemNameValue',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '63'
+ }
+ }
+ },
+ {
+ 'type': 'button',
+ 'name': 'icon_ellipses',
+ 'class': 'node-button',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '33',
+ 'y': '-35'
+ }
+ },
+ 'svgAttributes': {
+ 'className': 'node-button',
+ 'r': '10'
+ }
+ },
+ {
+ 'type': 'button',
+ 'name': 'icon_triangle_warning',
+ 'class': 'node-button',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '46',
+ 'y': '-12'
+ }
+ },
+ 'svgAttributes': {
+ 'className': 'node-button',
+ 'r': '10'
+ }
+ }
+ ]
+ },
+ 'selectedNodeClass': {
+ 'class': 'aai-entity-node selected-node',
+ 'visualElements': [
+ {
+ 'type': 'circle',
+ 'class': 'outer',
+ 'svgAttributes': {
+ 'r': '31'
+ }
+ },
+ {
+ 'type': 'circle',
+ 'class': 'inner',
+ 'svgAttributes': {
+ 'r': '20'
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-type-label',
+ 'displayKey': 'itemType',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '48'
+ }
+ }
+ },
+ {
+ 'type': 'text',
+ 'class': 'id-value-label',
+ 'displayKey': 'itemNameValue',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '0',
+ 'y': '63'
+ }
+ }
+ },
+ {
+ 'type': 'button',
+ 'name': 'icon_ellipses',
+ 'class': 'node-button',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '33',
+ 'y': '-35'
+ }
+ },
+ 'svgAttributes': {
+ 'className': 'node-button',
+ 'r': '10'
+ }
+ },
+ {
+ 'type': 'button',
+ 'name': 'icon_triangle_warning',
+ 'class': 'node-button',
+ 'shapeAttributes': {
+ 'offset': {
+ 'x': '46',
+ 'y': '-12'
+ }
+ },
+ 'svgAttributes': {
+ 'className': 'node-button',
+ 'r': '10'
+ }
+ }
+ ]
+ }
+ },
+ 'numNodes': 6,
+ 'numLinks': 5,
+ 'renderTimeInMs': 4550,
+ 'numLinksResolvedSuccessfullyFromCache': 0,
+ 'numLinksResolvedSuccessfullyFromServer': 7,
+ 'numLinkResolveFailed': 0
+ },
+ 'nodes': [{
+ 'id': 'TRINITY-PSERVER',
+ 'itemType': 'pserver',
+ 'itemNameKey': 'pserver.TRINITY-PSERVER',
+ 'itemNameValue': 'TRINITY-PSERVER',
+ 'itemProperties': {
+ 'hostname': 'TRINITY-PSERVER',
+ 'in-maint': 'false',
+ 'resource-version': '1455590484'
+ },
+ 'nodeMeta': {
+ 'className': 'selectedSearchedNodeClass',
+ 'nodeDebug': null,
+ 'selfLinkResponseTimeInMs': 628,
+ 'relationshipNode': false,
+ 'searchTarget': true,
+ 'enrichableNode': false,
+ 'nodeValidated': true,
+ 'nodeIssue': true
+ }
+ }, {
+ 'id': 'TRINITYSIL',
+ 'itemType': 'complex',
+ 'itemNameKey': 'complex.TRINITYSIL',
+ 'itemNameValue': 'TRINITYSIL',
+ 'itemProperties': {
+ 'country': 'USA',
+ 'postal-code': '07748',
+ 'city': 'Middletown',
+ 'physical-location-id': 'TRINITYSIL',
+ 'resource-version': '1459957457',
+ 'street1': 'Trinity',
+ 'state': 'NJ',
+ 'physical-location-type': 'Trinity',
+ 'region': 'US'
+ },
+ 'nodeMeta': {
+ 'className': 'generalNodeClass',
+ 'nodeDebug': null,
+ 'selfLinkResponseTimeInMs': 644,
+ 'relationshipNode': false,
+ 'searchTarget': false,
+ 'enrichableNode': false,
+ 'nodeValidated': true,
+ 'nodeIssue': false
+ }
+ }, {
+ 'id': 'c385bb3e-6ebd-4898-bc92-792e0ac2db50',
+ 'itemType': 'vserver',
+ 'itemNameKey': 'vserver.c385bb3e-6ebd-4898-bc92-792e0ac2db50',
+ 'itemNameValue': 'c385bb3e-6ebd-4898-bc92-792e0ac2db50',
+ 'itemProperties': {
+ 'in-maint': 'false',
+ 'resource-version': '1475160142',
+ 'vserver-name': 'bems0001vm001',
+ 'prov-status': 'ACTIVE',
+ 'vserver-id': 'c385bb3e-6ebd-4898-bc92-792e0ac2db50',
+ 'vserver-name2': 'bems0001vm001bem001-1452',
+ 'vserver-selflink': 'TRINITY vserverLink',
+ 'is-closed-loop-disabled': 'false'
+ },
+ 'nodeMeta': {
+ 'className': 'generalNodeClass',
+ 'nodeDebug': null,
+ 'selfLinkResponseTimeInMs': 2633,
+ 'relationshipNode': false,
+ 'searchTarget': false,
+ 'enrichableNode': false
+ }
+ }, {
+ 'id': '7c73d776-001d-4042-a958-37f2e419ed10',
+ 'itemType': 'vserver',
+ 'itemNameKey': 'vserver.7c73d776-001d-4042-a958-37f2e419ed10',
+ 'itemNameValue': '7c73d776-001d-4042-a958-37f2e419ed10',
+ 'itemProperties': {
+ 'resource-version': '1477075390',
+ 'vserver-name': 'nsbg0001vm002',
+ 'prov-status': 'NVTPROV',
+ 'vserver-id': '7c73d776-001d-4042-a958-37f2e419ed10',
+ 'vserver-name2': 'VM-19631',
+ 'vserver-selflink': 'TRINITY vserverLink'
+ },
+ 'nodeMeta': {
+ 'className': 'generalNodeClass',
+ 'nodeDebug': null,
+ 'selfLinkResponseTimeInMs': 2368,
+ 'relationshipNode': false,
+ 'searchTarget': false,
+ 'enrichableNode': false
+ }
+ }, {
+ 'id': 'fc6be93d-915e-4034-a8f9-463b70130614',
+ 'itemType': 'vserver',
+ 'itemNameKey': 'vserver.fc6be93d-915e-4034-a8f9-463b70130614',
+ 'itemNameValue': 'fc6be93d-915e-4034-a8f9-463b70130614',
+ 'itemProperties': {
+ 'resource-version': '1477075398',
+ 'vserver-name': 'nsbg0001vm004',
+ 'prov-status': 'NVTPROV',
+ 'vserver-id': 'fc6be93d-915e-4034-a8f9-463b70130614',
+ 'vserver-name2': 'VM-19630',
+ 'vserver-selflink': 'TRINITY vserverLink'
+ },
+ 'nodeMeta': {
+ 'className': 'generalNodeClass',
+ 'nodeDebug': null,
+ 'selfLinkResponseTimeInMs': 2621,
+ 'relationshipNode': false,
+ 'searchTarget': false,
+ 'enrichableNode': false
+ }
+ }, {
+ 'id': '8555c2ed-6818-43c5-8cf5-cd36b0169031',
+ 'itemType': 'vserver',
+ 'itemNameKey': 'vserver.8555c2ed-6818-43c5-8cf5-cd36b0169031',
+ 'itemNameValue': '8555c2ed-6818-43c5-8cf5-cd36b0169031',
+ 'itemProperties': {
+ 'resource-version': '1477075396',
+ 'vserver-name': 'nsbg0001vm003',
+ 'prov-status': 'NVTPROV',
+ 'vserver-id': '8555c2ed-6818-43c5-8cf5-cd36b0169031',
+ 'vserver-name2': 'VM-19629',
+ 'vserver-selflink': 'TRINITY vserverLink'
+ },
+ 'nodeMeta': {
+ 'className': 'generalNodeClass',
+ 'nodeDebug': null,
+ 'selfLinkResponseTimeInMs': 2663,
+ 'relationshipNode': false,
+ 'searchTarget': false,
+ 'enrichableNode': false
+ }
+ }],
+ 'links': [{
+ 'id': 'TRINITY-PSERVER_TRINITYSIL',
+ 'source': 'TRINITY-PSERVER',
+ 'target': 'TRINITYSIL'
+ }, {
+ 'id': 'TRINITY-PSERVER_c385bb3e-6ebd-4898-bc92-792e0ac2db50',
+ 'source': 'TRINITY-PSERVER',
+ 'target': 'c385bb3e-6ebd-4898-bc92-792e0ac2db50'
+ }, {
+ 'id': 'TRINITY-PSERVER_7c73d776-001d-4042-a958-37f2e419ed10',
+ 'source': 'TRINITY-PSERVER',
+ 'target': '7c73d776-001d-4042-a958-37f2e419ed10'
+ }, {
+ 'id': 'TRINITY-PSERVER_fc6be93d-915e-4034-a8f9-463b70130614',
+ 'source': 'TRINITY-PSERVER',
+ 'target': 'fc6be93d-915e-4034-a8f9-463b70130614'
+ }, {
+ 'id': 'TRINITY-PSERVER_8555c2ed-6818-43c5-8cf5-cd36b0169031',
+ 'source': 'TRINITY-PSERVER',
+ 'target': '8555c2ed-6818-43c5-8cf5-cd36b0169031'
+ }]
+ };
+ }
+};
diff --git a/src/generic-components/treeNode/TreeNode.jsx b/src/generic-components/treeNode/TreeNode.jsx
new file mode 100644
index 0000000..b5fc3cc
--- /dev/null
+++ b/src/generic-components/treeNode/TreeNode.jsx
@@ -0,0 +1,79 @@
+/*
+ * ============LICENSE_START===================================================
+ * SPARKY (AAI UI service)
+ * ============================================================================
+ * Copyright © 2017 AT&T Intellectual Property.
+ * Copyright © 2017 Amdocs
+ * 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=====================================================
+ *
+ * ECOMP and OpenECOMP are trademarks
+ * and service marks of AT&T Intellectual Property.
+ */
+
+import React, {Component} from 'react';
+import classNames from 'classnames';
+
+
+
+
+class TreeNode extends Component {
+
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ visible: false,
+ };
+ }
+
+ toggle = () => {
+ this.setState({visible: !this.state.visible});
+ };
+
+ render() {
+ var childNodes;
+ var classObj;
+ if (this.props.node !== undefined && this.props.node.childNodes !== undefined) {
+ childNodes = this.props.node.childNodes.map(function (node, index) {
+ return <li key={index}><TreeNode node={node}/></li>;
+ });
+
+ classObj = {
+ togglable: true,
+ 'togglable-down': this.state.visible,
+ 'togglable-up': !this.state.visible
+ };
+ }
+
+ var style;
+ if (!this.state.visible) {
+ style = {display: 'none'};
+ }
+
+ return (
+ <div>
+ <h7 onClick={this.toggle} className={classNames(classObj)}>
+ {this.props.node.title}
+ </h7>
+ <ul style={style} className='node-tree'>
+ {childNodes}
+ </ul>
+ </div>
+ );
+ }
+}
+
+export default TreeNode;