RIFT-15154: Config parameter map component, intial pass.

Signed-off-by: Laurence Maultsby <laurence.maultsby@riftio.com>
diff --git a/skyquake/framework/core/modules/routes/inactivity.js b/skyquake/framework/core/modules/routes/inactivity.js
index 7c3c440..a90258d 100644
--- a/skyquake/framework/core/modules/routes/inactivity.js
+++ b/skyquake/framework/core/modules/routes/inactivity.js
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/skyquake/plugins/composer/src/src/components/ConfigPrimitiveParameters/ConfigPrimitiveParameters.js b/skyquake/plugins/composer/src/src/components/ConfigPrimitiveParameters/ConfigPrimitiveParameters.js
index 3621061..848f0b4 100644
--- a/skyquake/plugins/composer/src/src/components/ConfigPrimitiveParameters/ConfigPrimitiveParameters.js
+++ b/skyquake/plugins/composer/src/src/components/ConfigPrimitiveParameters/ConfigPrimitiveParameters.js
@@ -49,7 +49,7 @@
 import imgConnection from '../../../../node_modules/open-iconic/svg/random.svg'
 import imgClassifier from '../../../../node_modules/open-iconic/svg/spreadsheet.svg'
 import imgReorder from '../../../../node_modules/open-iconic/svg/menu.svg'
-import EditDescriptorModelProperties from '../EditDescriptorModelProperties'
+import EditConfigParameterMap from '../EditConfigParameterMap'
 function configParameterMapMap(ap, i) {
 
     const context = this;
@@ -214,7 +214,7 @@
                     {
                         containers.map(function(c, i) {
                             if(c.className == 'ConfigParameterMap') {
-                                return <EditDescriptorModelProperties key={i} container={c} width={self.props.width} />
+                                return <EditConfigParameterMap key={i} container={c} width={self.props.width} />
                             }
                         })
                     }
diff --git a/skyquake/plugins/composer/src/src/components/EditConfigParameterMap.js b/skyquake/plugins/composer/src/src/components/EditConfigParameterMap.js
new file mode 100644
index 0000000..1add58c
--- /dev/null
+++ b/skyquake/plugins/composer/src/src/components/EditConfigParameterMap.js
@@ -0,0 +1,685 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+/**
+ * Created by onvelocity on 1/18/16.
+ *
+ * This class generates the form fields used to edit the CONFD JSON model.
+ */
+'use strict';
+
+import _ from 'lodash'
+import utils from '../libraries/utils'
+import React from 'react'
+import ClassNames from 'classnames'
+import changeCase from 'change-case'
+import toggle from '../libraries/ToggleElementHandler'
+import Button from './Button'
+import Property from '../libraries/model/DescriptorModelMetaProperty'
+import ComposerAppActions from '../actions/ComposerAppActions'
+import CatalogItemsActions from '../actions/CatalogItemsActions'
+import DESCRIPTOR_MODEL_FIELDS from '../libraries/model/DescriptorModelFields'
+import DescriptorModelFactory from '../libraries/model/DescriptorModelFactory'
+import DescriptorModelMetaFactory from '../libraries/model/DescriptorModelMetaFactory'
+import SelectionManager from '../libraries/SelectionManager'
+import DeletionManager from '../libraries/DeletionManager'
+import DescriptorModelIconFactory from '../libraries/model/IconFactory'
+import getEventPath from '../libraries/getEventPath'
+import CatalogDataStore from '../stores/CatalogDataStore'
+
+import imgAdd from '../../../node_modules/open-iconic/svg/plus.svg'
+import imgRemove from '../../../node_modules/open-iconic/svg/trash.svg'
+
+import '../styles/EditDescriptorModelProperties.scss'
+
+
+
+function getDescriptorMetaBasicForType(type) {
+	const basicPropertiesFilter = d => _.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+	return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
+}
+
+function getDescriptorMetaAdvancedForType(type) {
+	const advPropertiesFilter = d => !_.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+	return DescriptorModelMetaFactory.getModelMetaForType(type, advPropertiesFilter) || {properties: []};
+}
+
+function getTitle(model = {}) {
+	if (typeof model['short-name'] === 'string' && model['short-name']) {
+		return model['short-name'];
+	}
+	if (typeof model.name === 'string' && model.name) {
+		return model.name;
+	}
+	if (model.uiState && typeof model.uiState.displayName === 'string' && model.uiState.displayName) {
+		return model.uiState.displayName
+	}
+	if (typeof model.id === 'string') {
+		return model.id;
+	}
+}
+function startEditing() {
+		DeletionManager.removeEventListeners();
+	}
+
+	function endEditing() {
+		DeletionManager.addEventListeners();
+	}
+
+	function onClickSelectItem(property, path, value, event) {
+		event.preventDefault();
+		const root = this.getRoot();
+		if (SelectionManager.select(value)) {
+			CatalogItemsActions.catalogItemMetaDataChanged(root.model);
+		}
+	}
+
+	function onFocusPropertyFormInputElement(property, path, value, event) {
+
+		event.preventDefault();
+		startEditing();
+
+		function removeIsFocusedClass(event) {
+			event.target.removeEventListener('blur', removeIsFocusedClass);
+			Array.from(document.querySelectorAll('.-is-focused')).forEach(d => d.classList.remove('-is-focused'));
+		}
+
+		removeIsFocusedClass(event);
+
+		const propertyWrapper = getEventPath(event).reduce((parent, element) => {
+			if (parent) {
+				return parent;
+			}
+			if (!element.classList) {
+				return false;
+			}
+			if (element.classList.contains('property')) {
+				return element;
+			}
+		}, false);
+
+		if (propertyWrapper) {
+			propertyWrapper.classList.add('-is-focused');
+			event.target.addEventListener('blur', removeIsFocusedClass);
+		}
+
+	}
+
+	function buildAddPropertyAction(container, property, path) {
+		function onClickAddProperty(property, path, event) {
+			event.preventDefault();
+			//SelectionManager.resume();
+			const create = Property.getContainerCreateMethod(property, this);
+			if (create) {
+				const model = null;
+				create(model, path, property);
+			} else {
+				const name = path.join('.');
+				const value = Property.createModelInstance(property);
+				utils.assignPathValue(this.model, name, value);
+			}
+			CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+		}
+		return (
+				<Button className="inline-hint" onClick={onClickAddProperty.bind(container, property, path)} label="Add" src={imgAdd} />
+		);
+	}
+
+	function buildRemovePropertyAction(container, property, path) {
+		function onClickRemoveProperty(property, path, event) {
+			event.preventDefault();
+			const name = path.join('.');
+			const removeMethod = Property.getContainerMethod(property, this, 'remove');
+			if (removeMethod) {
+				removeMethod(utils.resolvePath(this.model, name));
+			} else {
+				utils.removePathValue(this.model, name);
+			}
+			CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+		}
+		return (
+			<Button className="remove-property-action inline-hint" title="Remove" onClick={onClickRemoveProperty.bind(container, property, path)} label="Remove" src={imgRemove}/>
+		);
+	}
+
+	function onFormFieldValueChanged(event) {
+		if (DescriptorModelFactory.isContainer(this)) {
+			event.preventDefault();
+			const name = event.target.name;
+			const value = event.target.value;
+			utils.assignPathValue(this.model, name, value);
+			CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+		}
+	}
+
+	function buildField(container, property, path, value, fieldKey) {
+		let cds = CatalogDataStore;
+		let catalogs = cds.getTransientCatalogs();
+
+		const name = path.join('.');
+		const isEditable = true;
+		const isGuid = Property.isGuid(property);
+		const onChange = onFormFieldValueChanged.bind(container);
+		const isEnumeration = Property.isEnumeration(property);
+		const isLeafRef = Property.isLeafRef(property);
+		const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
+		const placeholder = changeCase.title(property.name);
+		const className = ClassNames(property.name + '-input', {'-is-guid': isGuid});
+		const fieldValue = value ? (value.constructor.name != "Object") ? value : '' : undefined;
+		if (isEnumeration) {
+			const enumeration = Property.getEnumeration(property, value);
+			const options = enumeration.map((d, i) => {
+				// note yangforge generates values for enums but the system does not use them
+				// so we categorically ignore them
+				// https://trello.com/c/uzEwVx6W/230-bug-enum-should-not-use-index-only-name
+				//return <option key={fieldKey + ':' + i} value={d.value}>{d.name}</option>;
+				return <option key={fieldKey.toString() + ':' + i} value={d.name}>{d.name}</option>;
+			});
+			const isValueSet = enumeration.filter(d => d.isSelected).length > 0;
+			if (!isValueSet || property.cardinality === '0..1') {
+				const noValueDisplayText = changeCase.title(property.name);
+				options.unshift(<option key={'(value-not-in-enum)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+			}
+			return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+		}
+
+		if (isLeafRef) {
+			let fullFieldKey = _.isArray(fieldKey) ? fieldKey.join(':') : fieldKey;
+			let containerRef = container;
+			while (containerRef.parent) {
+				fullFieldKey = containerRef.parent.key + ':' + fullFieldKey;
+				containerRef = containerRef.parent;
+			}
+			const leafRefPathValues = Property.getLeafRef(property, path, value, fullFieldKey, catalogs, container);
+
+			const options = leafRefPathValues && leafRefPathValues.map((d, i) => {
+				return <option key={fieldKey.toString() + ':' + i} value={d.value}>{d.value}</option>;
+			});
+			const isValueSet = leafRefPathValues.filter(d => d.isSelected).length > 0;
+			if (!isValueSet || property.cardinality === '0..1') {
+				const noValueDisplayText = changeCase.title(property.name);
+				options.unshift(<option key={'(value-not-in-leafref)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+			}
+			return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+		}
+
+		if (property['preserve-line-breaks']) {
+			return <textarea key={fieldKey.toString()} cols="5" id={fieldKey.toString()} name={name} value={value} placeholder={placeholder} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing} readOnly={!isEditable} />;
+		}
+
+		return <input key={fieldKey.toString()}
+					  id={fieldKey.toString()}
+					  type="text"
+					  name={name}
+					  value={fieldValue}
+					  className={className}
+					  placeholder={placeholder}
+					  onChange={onChange}
+					  onFocus={onFocus}
+					  onBlur={endEditing}
+					  onMouseDown={startEditing}
+					  onMouseOver={startEditing}
+					  onMouseOut={endEditing}
+					  onMouseLeave={endEditing}
+					  readOnly={!isEditable}
+		/>;
+
+	}
+
+	function buildElement(container, property, valuePath, value) {
+		return property.properties.map((property, index) => {
+			let childValue;
+			const childPath = valuePath.slice();
+			if (typeof value === 'object') {
+				childValue = value[property.name];
+			}
+			if(property.type != 'choice'){
+						childPath.push(property.name);
+			}
+			return build(container, property, childPath, childValue);
+
+		});
+	}
+
+    function buildChoice(container, property, path, value, key, props={}) {
+
+		function onFormFieldValueChanged(event) {
+			if (DescriptorModelFactory.isContainer(this)) {
+
+				event.preventDefault();
+
+				let name = event.target.name;
+				const value = event.target.value;
+
+
+				/*
+					Transient State is stored for convenience in the uiState field.
+					The choice yang type uses case elements to describe the "options".
+					A choice can only ever have one option selected which allows
+					the system to determine which type is selected by the name of
+					the element contained within the field.
+				 */
+				/*
+					const stateExample = {
+						uiState: {
+							choice: {
+								'conf-config': {
+									selected: 'rest',
+									'case': {
+										rest: {},
+										netconf: {},
+										script: {}
+									}
+								}
+							}
+						}
+					};
+				*/
+				const statePath = ['uiState.choice'].concat(name);
+				const stateObject = utils.resolvePath(this.model, statePath.join('.')) || {};
+				const selected = stateObject.selected ? stateObject.selected.split('.')[1] : undefined;
+				// write state back to the model so the new state objects are captured
+				utils.assignPathValue(this.model, statePath.join('.'), stateObject);
+
+				// write the current choice value into the state
+				let choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
+				let isTopCase = false;
+				if (!choiceObject) {
+					isTopCase = true;
+					choiceObject = utils.resolvePath(this.model, [selected].join('.'));
+				}
+				utils.assignPathValue(stateObject, [selected].join('.'), _.cloneDeep(choiceObject));
+
+				if(selected) {
+					if(this.model.uiState.choice.hasOwnProperty(name)) {
+						delete this.model[selected];
+						utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+					} else {
+						// remove the current choice value from the model
+						utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+					}
+				}
+
+				// get any state for the new selected choice
+				const newChoiceObject = utils.resolvePath(stateObject, [value].join('.')) || {};
+
+				// assign new choice value to the model
+				if (isTopCase) {
+					utils.assignPathValue(this.model, [name, value].join('.'), newChoiceObject);
+				} else {
+					utils.assignPathValue(this.model, [value].join('.'), newChoiceObject)
+				}
+
+
+				// update the selected name
+				utils.assignPathValue(this.model, statePath.concat('selected').join('.'), value);
+
+				CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+			}
+		}
+
+		const caseByNameMap = {};
+
+		const onChange = onFormFieldValueChanged.bind(container);
+
+		const cases = property.properties.map(d => {
+			if (d.type === 'case') {
+				caseByNameMap[d.name] = d.properties[0];
+				return {
+					optionName: d.name,
+					optionTitle: d.description,
+					//represents case name and case element name
+					optionValue: [d.name, d.properties[0].name].join('.')
+				};
+			}
+			caseByNameMap[d.name] = d;
+			return {optionName: d.name};
+		});
+
+		const options = [{optionName: '', optionValue: false}].concat(cases).map((d, i) => {
+			return (
+				<option key={i} value={d.optionValue} title={d.optionTitle}>
+					{d.optionName}
+					{i ? null : changeCase.title(property.name)}
+				</option>
+			);
+		});
+
+		const selectName = path.join('.');
+		let selectedOptionPath = ['uiState.choice', selectName, 'selected'].join('.');
+		//Currently selected choice/case statement on UI model
+		let selectedOptionValue = utils.resolvePath(container.model, selectedOptionPath);
+		//If first time loaded, and none is selected, check if there is a value corresponding to a case statement in the container model
+		if(!selectedOptionValue) {
+			//get field properties for choice on container model
+			let fieldProperties = utils.resolvePath(container.model, selectName);
+			if(fieldProperties) {
+				//Check each case statement in model and see if it is present in container model.
+				cases.map(function(c){
+					if(fieldProperties.hasOwnProperty(c.optionValue.split('.')[1])) {
+						utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), c.optionValue);
+					}
+				});
+				selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+			} else {
+				property.properties.map(function(p) {
+					let pname = p.properties[0].name;
+					if(container.model.hasOwnProperty(pname)) {
+						utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), [p.name, pname].join('.'));
+					}
+				})
+				selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+			}
+		}
+		//If selectedOptionValue is present, take first item in string which represents the case name.
+		const valueProperty = caseByNameMap[selectedOptionValue ? selectedOptionValue.split('.')[0] : undefined] || {properties: []};
+		const isLeaf = Property.isLeaf(valueProperty);
+		const hasProperties = _.isArray(valueProperty.properties) && valueProperty.properties.length;
+		const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(valueProperty);
+		//Some magic that prevents errors for arising
+		const valueResponse = valueProperty.properties.length ? valueProperty.properties.map((d, i) => {
+			const childPath = path.concat(valueProperty.name, d.name);
+			const childValue = utils.resolvePath(container.model, childPath.join('.'));
+			return (
+				<div key={childPath.concat('info', i).join(':')}>
+					{build(container, d, childPath, childValue, props)}
+				</div>
+			);
+		}) : (!isMissingDescriptorMeta) ? build(container, valueProperty, path.concat(valueProperty.name), utils.resolvePath(container.model, path.concat(valueProperty.name).join('.')) || container.model[valueProperty.name]) : null
+		// end magic
+		const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
+
+		return (
+			<div key={key} className="choice">
+				<select key={Date.now()} className={ClassNames({'-value-not-set': !selectedOptionValue})} name={selectName} value={selectedOptionValue} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing}>
+					{options}
+				</select>
+				{valueResponse}
+			</div>
+		);
+
+	}
+
+	function buildSimpleListItem(container, property, path, value, key, index) {
+		// todo need to abstract this better
+		const title = getTitle(value);
+		var req = require.context("../", true, /\.svg/);
+		return (
+			<div>
+				<a href="#select-list-item" key={Date.now()} className={property.name + '-list-item simple-list-item '} onClick={onClickSelectItem.bind(container, property, path, value)}>
+					<img src={req('./' + DescriptorModelIconFactory.getUrlForType(property.name))} width="20px" />
+					<span>{title}</span>
+				</a>
+				{buildRemovePropertyAction(container, property, path)}
+			</div>
+		);
+	}
+
+	function buildRemoveListItem(container, property, valuePath, fieldKey, index) {
+		const className = ClassNames(property.name + '-remove actions');
+		return (
+			<div key={fieldKey.concat(index).join(':')} className={className}>
+				<h3>
+					<span className={property.type + '-name name'}>{changeCase.title(property.name)}</span>
+					<span className="info">{index + 1}</span>
+					{buildRemovePropertyAction(container, property, valuePath)}
+				</h3>
+			</div>
+		);
+	}
+
+	function buildLeafListItem(container, property, valuePath, value, key, index) {
+		// look at the type to determine how to parse the value
+		return (
+			<div>
+				{buildRemoveListItem(container, property, valuePath, key, index)}
+				{buildField(container, property, valuePath, value, key)}
+			</div>
+
+		);
+	}
+
+	function build(container, property, path, value, props = {}) {
+
+		const fields = [];
+		const isLeaf = Property.isLeaf(property);
+		const isArray = Property.isArray(property);
+		const isObject = Property.isObject(property);
+		const isLeafList = Property.isLeafList(property);
+		const fieldKey = [container.id].concat(path);
+		const isRequired = Property.isRequired(property);
+		const title = changeCase.titleCase(property.name);
+		const columnCount = property.properties.length || 1;
+		const isColumnar = isArray && (Math.round(props.width / columnCount) > 155);
+		const classNames = {'-is-required': isRequired, '-is-columnar': isColumnar};
+
+		if (!property.properties && isObject) {
+			const uiState = DescriptorModelMetaFactory.getModelMetaForType(property.name) || {};
+			property.properties = uiState.properties;
+		}
+
+		const hasProperties = _.isArray(property.properties) && property.properties.length;
+		const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(property);
+
+		// ensure value is not undefined for non-leaf property types
+		if (isObject) {
+			if (typeof value !== 'object') {
+				value = isArray ? [] : {};
+			}
+		}
+		const valueAsArray = _.isArray(value) ? value : isLeafList && typeof value === 'undefined' ? [] : [value];
+
+		const isMetaField = property.name === 'meta';
+		const isCVNFD = property.name === 'constituent-vnfd';
+		const isSimpleListView = Property.isSimpleList(property);
+
+		valueAsArray.forEach((value, index) => {
+
+			let field;
+			const key = fieldKey.slice();
+			const valuePath = path.slice();
+
+			if (isArray) {
+				valuePath.push(index);
+				key.push(index);
+			}
+
+			if (isMetaField) {
+				if (typeof value === 'object') {
+					value = JSON.stringify(value, undefined, 12);
+				} else if (typeof value !== 'string') {
+					value = '{}';
+				}
+			}
+
+			if (isMissingDescriptorMeta) {
+				field = <span key={key.concat('warning').join(':')} className="warning">No Descriptor Meta for {property.name}</span>;
+			} else if (property.type === 'choice') {
+                field = buildChoice(container, property, valuePath, value, key.join(':'), props);
+			} else if (isSimpleListView) {
+                field = buildSimpleListItem(container, property, valuePath, value, key, index, props);
+			} else if (isLeafList) {
+                field = buildLeafListItem(container, property, valuePath, value, key, index, props);
+			} else if (hasProperties) {
+                field = buildElement(container, property, valuePath, value, key.join(':'), props)
+			} else {
+                field = buildField(container, property, valuePath, value, key.join(':'), props);
+			}
+
+			function onClickLeaf(property, path, value, event) {
+				if (event.isDefaultPrevented()) {
+					return;
+				}
+				event.preventDefault();
+				event.stopPropagation();
+				this.getRoot().uiState.focusedPropertyPath = path.join('.');
+				console.log('property selected', path.join('.'));
+				ComposerAppActions.propertySelected([path.join('.')]);
+			}
+
+			const clickHandler = isLeaf ? onClickLeaf : () => {};
+			const isContainerList = isArray && !(isSimpleListView || isLeafList);
+
+			fields.push(
+				<div key={fieldKey.concat(['property-content', index]).join(':')}
+					 className={ClassNames('property-content', {'simple-list': isSimpleListView})}
+					 onClick={clickHandler.bind(container, property, valuePath, value)}>
+					{isContainerList ? buildRemoveListItem(container, property, valuePath, fieldKey, index) : null}
+					{field}
+				</div>
+			);
+
+		});
+
+		classNames['-is-leaf'] = isLeaf;
+		classNames['-is-array'] = isArray;
+		classNames['cols-' + columnCount] = isColumnar;
+
+		if (property.type === 'choice') {
+			value = utils.resolvePath(container.model, ['uiState.choice'].concat(path, 'selected').join('.'));
+			if(!value) {
+				property.properties.map(function(p) {
+					let pname = p.properties[0].name;
+					if(container.model.hasOwnProperty(pname)) {
+						value = container.model[pname];
+					}
+				})
+			}
+		}
+
+		let displayValue = typeof value === 'object' ? '' : value;
+		const displayValueInfo = isArray ? valueAsArray.filter(d => typeof d !== 'undefined').length + ' items' : '';
+
+		const onFocus = isLeaf ? event => event.target.classList.add('-is-focused') : false;
+
+		return (
+			<div key={fieldKey.join(':')} className={ClassNames(property.type + '-property property', classNames)} onFocus={onFocus}>
+				<h3 className="property-label">
+					<label htmlFor={fieldKey.join(':')}>
+						<span className={property.type + '-name name'}>{title}</span>
+						<small>
+							<span className={property.type + '-info info'}>{displayValueInfo}</span>
+							<span className={property.type + '-value value'}>{displayValue}</span>
+						</small>
+						{isArray ? buildAddPropertyAction(container, property, path.concat(valueAsArray.length)) : null}
+					</label>
+				</h3>
+				<span className={property.type + '-description description'}>{property.description}</span>
+				<val className="property-value">
+					{isCVNFD ? <span className={property.type + '-tip tip'}>Drag a VNFD from the Catalog to add more.</span> : null}
+					{fields}
+				</val>
+			</div>
+		);
+
+	}
+export default function EditDescriptorModelProperties(props, type) {
+
+    const container = props.container;
+
+    if (!(DescriptorModelFactory.isContainer(container))) {
+        return
+    }
+
+
+
+    const containerType = (_.isEmpty(type) ? false : type)|| container.uiState['qualified-type'] || container.uiState.type;
+	const basicProperties = getDescriptorMetaBasicForType(containerType).properties;
+
+
+	function buildAdvancedGroup() {
+		const properties = getDescriptorMetaAdvancedForType(containerType).properties;
+		if (properties.length === 0) {
+			return null;
+		}
+		const hasBasicFields = basicProperties.length > 0;
+		const closeGroup = basicProperties.length > 0;
+		return (
+			<div className="advanced-properties-group">
+
+				<div className="toggleable">
+					{properties.map((property,i) => {
+						const path = [property.name];
+						const value = container.model[property.name];
+						if(path == 'id') {
+							return null
+						}
+						if(path == 'config-parameter-request') {
+							return (
+								<div className="container-property property" key={path + '-' + i}>
+									<h3 className="property-label">
+										<span className="container-name name">{`VNF Index: ${value['member-vnf-index-ref']}`}</span>
+									</h3>
+									<val className="property-value">
+										<div className="property-content">
+											<val className="property-value" style={{width: '100%'}}>
+												<div className="property-content">
+													<div className="leaf-property property -is-leaf">
+														<h3 className="property-label">
+															<label for={path + '-' + i}>
+																<span className="leaf-name name">
+																	Parameter Request
+																</span>
+															</label>
+														</h3>
+														<input value={value['config-parameter-request-ref']} readonly placeholder="Parameter Request" name={path + '-' + i}>
+
+														</input>
+													</div>
+												</div>
+											</val>
+										</div>
+									</val>
+
+								</div>
+							)
+						} else {
+							return build(container, property, path, value, _.assign({toggle: true, width: props.width}, props));
+						}
+
+					})}
+				</div>
+				<div className="toggle-bottom-spacer" style={{visibility: 'hidden', 'height': '50%', position: 'absolute'}}>We need this so when the user closes the panel it won't shift away and scare the bj out of them!</div>
+			</div>
+		);
+	}
+
+	function buildMoreLess(d, i) {
+		return (
+			<span key={'bread-crumb-part-' + i}>
+				<a href="#select-item" onClick={onClickSelectItem.bind(d, null, null, d)}>{d.title}</a>
+				<i> / </i>
+			</span>
+		);
+	}
+
+	const path = [];
+	if (container.parent) {
+		path.push(container.parent);
+	}
+	path.push(container);
+
+	return (
+		<div className="EditDescriptorModelProperties -is-tree-view">
+			{buildAdvancedGroup()}
+		</div>
+	);
+
+}
+export {build}
+// export buildElement;
+// export buildChoice;
diff --git a/skyquake/plugins/composer/src/src/styles/CanvasPanelTray.scss b/skyquake/plugins/composer/src/src/styles/CanvasPanelTray.scss
index 55844b6..899eb8f 100644
--- a/skyquake/plugins/composer/src/src/styles/CanvasPanelTray.scss
+++ b/skyquake/plugins/composer/src/src/styles/CanvasPanelTray.scss
@@ -30,7 +30,7 @@
 	height: 25px;
 	min-width: 300px;
 	/* background-color: white;*/
-	background: #cbd1d1;
+	background: $panel-bg-color;
 	&.-with-transitions {
 		transition: height 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000);
 	}
@@ -65,14 +65,17 @@
 	&-buttons {
 		display: -ms-flexbox;
 		display: flex;
-		margin-top: 1px;
+		margin-top: 2px;
+		button {
+			padding: 6px 34px;
+		}
 	}
 	.tray-body {
 		top:31px;
 	}
 	.ConfigParameterMap {
 
-		background: #cbd1d1;
+		background: $panel-bg-color;
 
 		.EditDescriptorModelProperties {
 		    margin-left: 8px;
@@ -83,6 +86,8 @@
 			display:flex;
 			-ms-flex-wrap: wrap;
 			    flex-wrap: wrap;
+		    background: $panel-bg-color-contrast;
+		    margin:8px 0;
 		 	& > .leaf-property {
 			    -ms-flex: 1 0 100%;
 		        flex: 1 0 100%;