JSCS Cleanup - Metadata widgets cleanup

Split directives and controllers into separate files.
Group widgets into higher level metadata module.
Apply code cleanup.

Co-Authored-By: Nathan Zeplowitz <nzeplowi@thoughtworks.com>
Co-Authored-By: Shaoquan Chen <sean.chen2@hp.com>

Partially-Implements: blueprint jscs-cleanup
Change-Id: I7b71f04af958e4ee9cd4404679d69e7ef0c40a68
This commit is contained in:
Szymon Wroblewski 2015-07-20 20:50:39 +02:00 committed by Shaoquan Chen
parent d06c3582d0
commit a340c79b75
27 changed files with 795 additions and 548 deletions

View File

@ -1,50 +0,0 @@
/*
* 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.
*/
(function () {
'use strict';
/**
* @ngdoc overview
* @name horizon.framework.widgets.metadata-display
* @description
*
* # horizon.framework.widgets.metadata-display
*
* The `horizon.framework.widgets.metadata-display` provides widget displaying metadata.
*
* | Directives |
* |---------------------------------------------------------------------------------------------|
* | {@link horizon.framework.widgets.metadata-display.directive:hzMetadataDisplay `hzMetadataDisplay`} |
* |---------------------------------------------------------------------------------------------|
* | Controllers |
* |---------------------------------------------------------------------------------------------|
* | {@link horizon.framework.widgets.metadata-display.controller:HzMetadataDisplayController `HzMetadataDisplayController`} |
*
*/
angular
.module('horizon.framework.widgets.metadata-display', [
'horizon.framework.widgets.metadata-tree'
])
/**
* @ngdoc parameters
* @name horizon.framework.widgets.metadata-display:metadataTreeDefaults
* @param {object} text Text constants
*/
.constant('horizon.framework.widgets.metadata-display.defaults', {
text: {
detail: gettext('Detail Information')
}
});
})();

View File

@ -1,115 +0,0 @@
<div class="metadata-tree-item" ng-form="itemForm">
<div class="input-group input-group-sm"
ng-switch on="item.leaf.type"
ng-if="item.leaf.type !== 'array'"
ng-class="{'has-error': itemForm.property.$invalid && itemForm.property.$dirty}">
<span class="input-group-addon"
title="{$ ::item.leaf.name $}"
ng-bind="::item.leaf.name">
</span>
<input ng-switch-when="string"
ng-if="!item.leaf.enum"
name="property"
type="text"
class="form-control"
pattern="{$ ::item.leaf.pattern $}"
required
ng-model="item.leaf.value"
ng-minlength="{$ ::item.leaf.minLength $}"
ng-maxlength="{$ ::item.leaf.maxLength $}"
ng-disabled="item.leaf.readonly"/>
<select ng-switch-when="string"
ng-if="item.leaf.enum"
name="property"
class="form-control"
required
ng-model="item.leaf.value"
ng-options="op for op in item.leaf.enum"
ng-disabled="item.leaf.readonly">
</select>
<input ng-switch-when="integer"
name="property"
type="number"
class="form-control"
required
ng-model="item.leaf.value"
ng-pattern="/^-?\d+$/"
min="{$ ::item.leaf.minimum $}"
max="{$ ::item.leaf.maximum $}"
step="1"
ng-disabled="item.leaf.readonly"/>
<input ng-switch-when="number"
name="property"
type="number"
class="form-control"
required
ng-model="item.leaf.value"
min="{$ ::item.leaf.minimum $}"
max="{$ ::item.leaf.maximum $}"
ng-disabled="item.leaf.readonly"/>
<div class="input-group-addon bool"
ng-switch-when="boolean">
<input name="property"
type="checkbox"
ng-model="item.leaf.value"
ng-disabled="item.leaf.readonly"/>
</div>
<div class="input-group-btn">
<a class="btn btn-default"
ng-click="action()">
<span class="fa fa-minus"></span>
</a>
</div>
</div>
<div ng-if="item.leaf.type === 'array'"
class="panel panel-default multiselect">
<div class="panel-heading">
<div ng-click="open()">
<span class="fa fa-fw"
ng-class="item.leaf.readonly ? '' : opened ? 'fa-chevron-down' : 'fa-chevron-right'">
</span>
<span ng-bind="::item.leaf.name"></span>
<div class="text-right">
<a class="btn btn-xs btn-default" ng-click="action()">
<i class="fa fa-minus"></i>
</a>
</div>
</div>
</div>
<div class="panel-body values"
ng-class="{disabled: item.leaf.readonly}">
<span ng-repeat="val in item.leaf.value">
<span class="label label-default"
ng-click="remove(val)">
<span ng-bind="::val"></span>
<span class="fa fa-times" ng-if="!item.leaf.readonly"></span>
</span>
</span>
</div>
<ul class="list-group options" ng-show="opened">
<li class="list-group-item"
ng-repeat="val in values"
ng-click="add(val)">
<span ng-bind="::val"></span>
</li>
</ul>
<div class="panel-footer" ng-show="opened">
<div class="form-inline clearfix">
<div class="form-group pull-right">
<label>Operator</label>
<select class="form-control input-sm"
ng-model="item.leaf.operator"
ng-options="val for val in item.leaf.operators">
</select>
</div>
</div>
</div>
</div>
<div class="label label-info">
<span ng-bind="::item.breadcrumb()"></span>
</div>
<div class="label label-danger"
ng-if="itemForm.$invalid && itemForm.$dirty">
<span ng-bind="formatErrorMessage(item, itemForm.property.$error)"></span>
</div>
</div>

View File

@ -1,242 +0,0 @@
(function () {
'use strict';
/**
* @ngdoc overview
* @name horizon.framework.widgets.metadata-tree
* @description
*
* # horizon.framework.widgets.metadata-tree
*
* The `horizon.framework.widgets.metadata-tree` provides widgets and service
* with logic for editing metadata.
*
* | Directives |
* |--------------------------------------------------------------------------------------------|
* | {@link horizon.framework.widgets.metadata-tree.directive:hzMetadataTree `hzMetadataTree`} |
* | {@link horizon.framework.widgets.metadata-tree.directive:hzMetadataTreeItem `hzMetadataTreeItem`} |
* | {@link horizon.framework.widgets.metadata-tree.directive:hzMetadataTreeUnique `hzMetadataTreeUnique`} |
* |--------------------------------------------------------------------------------------------|
* | Controllers |
* |--------------------------------------------------------------------------------------------|
* | {@link horizon.framework.widgets.metadata-tree.controller:hzMetadataTreeCtrl `hzMetadataTreeCtrl`} |
* | {@link horizon.framework.widgets.metadata-tree.controller:hzMetadataTreeItemCtrl `hzMetadataTreeItemCtrl`} |
*
*/
angular.module('horizon.framework.widgets.metadata-tree', [])
/**
* @ngdoc parameters
* @name horizon.framework.widgets.metadata-tree.constant:metadataTreeDefaults
* @param {object} text Text constants
*/
.constant('horizon.framework.widgets.metadata-tree.defaults', {
text: {
/*eslint-disable max-len */
help: gettext('You can specify resource metadata by moving items from the left column to the right column. In the left columns there are metadata definitions from the Glance Metadata Catalog. Use the "Other" option to add metadata with the key of your choice.'),
/*eslint-enable max-len */
min: gettext('Min'),
max: gettext('Max'),
minLength: gettext('Min length'),
maxLength: gettext('Max length'),
patternMismatch: gettext('Pattern mismatch'),
integerRequired: gettext('Integer required'),
decimalRequired: gettext('Decimal required'),
required: gettext('Required'),
duplicate: gettext('Duplicate keys are not allowed'),
filter: gettext('Filter'),
available: gettext('Available Metadata'),
existing: gettext('Existing Metadata'),
custom: gettext('Custom'),
noAvailable: gettext('No available metadata'),
noExisting: gettext('No existing metadata')
}
})
/**
* @ngdoc directive
* @name horizon.framework.widgets.metadata-tree.directive:hzMetadataTree
* @scope
*
* @description
* The `hzMetadataTree` directive provide support for modifying existing
* metadata properties and adding new from metadata catalog.
*
* @param {Tree=} model Model binding
* @param {object[]=} available List of available namespaces
* @param {object=} existing Key-value pairs with existing properties
* @param {object=} text Text override
*/
.directive('hzMetadataTree', [ 'horizon.framework.widgets.basePath',
function (path) {
return {
scope: {
tree: '=*?model',
available: '=?',
existing: '=?',
text: '=?'
},
controller: 'hzMetadataTreeCtrl',
templateUrl: path + 'metadata-tree/metadata-tree.html'
};
}
])
/**
* @ngdoc directive
* @name horizon.framework.widgets.metadata-tree.directive:hzMetadataTreeItem
* @scope
*
* @description
* The `hzMetadataTreeItem` helper directive displays proper field for
* editing Item.leaf.value depending on Item.leaf.type
*
* @param {expression} action Action for button
* @param {Item} item Item to display
* @param {object} text Text override
*/
.directive('hzMetadataTreeItem', [ 'horizon.framework.widgets.basePath',
function (path) {
return {
scope: {
action: '&',
item: '=',
text: '='
},
controller: 'hzMetadataTreeItemCtrl',
templateUrl: path + 'metadata-tree/metadata-tree-item.html'
};
}
])
/**
* @ngdoc directive
* @name horizon.framework.widgets.metadata-tree.directive:hzMetadataTreeUnique
* @restrict A
*
* @description
* The `hzMetadataTreeUnique` helper directive provides validation
* for field which value should be unique new Item
*/
.directive('hzMetadataTreeUnique', function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
ctrl.$validators.unique = function (modelValue, viewValue) {
return !scope.tree.flatTree.some(function (item) {
return item.leaf && item.leaf.name === viewValue;
});
};
}
};
})
/**
* @ngdoc controller
* @name horizon.framework.widgets.metadata-tree.controller:hzMetadataTreeCtrl
* @description
* Controller used by `hzMetadataTree`
*/
.controller('hzMetadataTreeCtrl', [
'$scope',
'horizon.framework.widgets.metadata-tree.service',
'horizon.framework.widgets.metadata-tree.defaults',
function ($scope, metadataTreeService, defaults) {
$scope.availableFilter = function (item) {
return !item.added && (
$scope.filterText.available.length === 0 ? item.visible : true);
};
$scope.text = angular.extend({}, defaults.text, $scope.text);
if (!$scope.tree) {
$scope.tree = new metadataTreeService.Tree($scope.available, $scope.existing);
}
$scope.customItem = '';
$scope.filterText = {
available: '',
existing: ''
};
}
])
/**
* @ngdoc controller
* @name horizon.framework.widgets.metadata-tree.controller:hzMetadataTreeItemCtrl
* @description
* Controller used by `hzMetadataTreeItem`
*/
.controller('hzMetadataTreeItemCtrl', [
'$scope',
function ($scope) {
$scope.formatErrorMessage = function (item, error) {
if (error.min) { return $scope.text.min + ' ' + item.leaf.minimum; }
if (error.max) { return $scope.text.max + ' ' + item.leaf.maximum; }
if (error.minlength) { return $scope.text.minLength + ' ' + item.leaf.minLength; }
if (error.maxlength) { return $scope.text.maxLength + ' ' + item.leaf.maxLength; }
if (error.pattern) {
if (item.leaf.type === 'integer') {
return $scope.text.integerRequired;
} else {
return $scope.text.patternMismatch;
}
}
if (error.number) {
if (item.leaf.type === 'integer') {
return $scope.text.integerRequired;
} else {
return $scope.text.decimalRequired;
}
}
if (error.required) {
return $scope.text.required;
}
};
function remove(array, value) {
var index = array.indexOf(value);
if (index > -1) {
array.splice(index, 1);
}
return array;
}
$scope.opened = false;
if ($scope.item.leaf.type === 'array') {
$scope.values = $scope.item.leaf.items.enum.filter(function (i) {
return $scope.item.leaf.value.indexOf(i) < 0;
}).sort();
if (!$scope.item.leaf.readonly) {
$scope.add = function (val) {
$scope.item.leaf.value.push(val);
$scope.item.leaf.value.sort();
remove($scope.values, val);
};
$scope.remove = function (val) {
remove($scope.item.leaf.value, val);
$scope.values.push(val);
$scope.values.sort();
if ($scope.item.leaf.value.length === 0) {
$scope.opened = true;
}
};
$scope.open = function () {
$scope.opened = !$scope.opened;
};
$scope.opened = $scope.item.leaf.value.length === 0;
}
}
}
]);
})();

View File

@ -0,0 +1,49 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
/*eslint-disable max-len */
/**
* @ngdoc overview
* @name horizon.framework.widgets.metadata.display
* @description
*
* # horizon.framework.widgets.metadata.display
*
* The `horizon.framework.widgets.metadata.display` provides widget displaying metadata.
*
* | Components |
* |---------------------------------------------------------------------------------------------|
* | {@link horizon.framework.widgets.metadata.display.directive:metadataDisplay `metadataDisplay`} |
* | {@link horizon.framework.widgets.metadata.display.controller:MetadataDisplayController `MetadataDisplayController`} |
*
*/
/*eslint-enable max-len */
angular
.module('horizon.framework.widgets.metadata.display', [], config);
config.$inject = [
'$provide',
'$windowProvider'
];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'framework/widgets/metadata/display/';
$provide.constant('horizon.framework.widgets.metadata.display.basePath', path);
}
})();

View File

@ -1,4 +1,6 @@
/*
* Copyright 2015, Intel Corp.
*
* 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
@ -14,9 +16,9 @@
(function () {
'use strict';
describe('horizon.framework.widgets.metadata-display module', function () {
describe('horizon.framework.widgets.metadata.display module', function () {
it('should have been defined', function () {
expect(angular.module('horizon.framework.widgets.metadata-display')).toBeDefined();
expect(angular.module('horizon.framework.widgets.metadata.display')).toBeDefined();
});
var namespaces = [
@ -102,13 +104,14 @@
'test:B:B:1': 'bar'
};
describe('hzMetadataDisplay directive', function () {
describe('metadataDisplay directive', function () {
var $scope, $element;
beforeEach(module('templates'));
beforeEach(module('horizon.framework.widgets'));
beforeEach(module('horizon.framework.widgets.metadata-tree'));
beforeEach(module('horizon.framework.widgets.metadata-display'));
beforeEach(module('horizon.framework.widgets.metadata'));
beforeEach(module('horizon.framework.widgets.metadata.tree'));
beforeEach(module('horizon.framework.widgets.metadata.display'));
beforeEach(inject(function ($injector) {
var $compile = $injector.get('$compile');
$scope = $injector.get('$rootScope').$new();
@ -116,14 +119,14 @@
$scope.available = namespaces;
$scope.existing = existing;
var markup = '<hz-metadata-display' +
var markup = '<metadata-display' +
' available="available"' +
' existing="existing">' +
'</hz-metadata-display>';
'</metadata-display>';
$element = angular.element(markup);
$compile($element)($scope);
$scope.$digest();
$scope.$apply();
}));
it('should have 3 rows in selector list', function () {

View File

@ -1,4 +1,6 @@
/*
* Copyright 2015, Intel Corp.
*
* 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
@ -14,26 +16,23 @@
(function () {
'use strict';
/**
* @ngdoc controller
* @name horizon.framework.widgets.metadata-display.controller:HzMetadataDisplayController
* @description
* Controller used by `hzMetadataDisplay`
*/
angular
.module('horizon.framework.widgets.metadata-display')
.controller('HzMetadataDisplayController', HzMetadataDisplayController);
.module('horizon.framework.widgets.metadata.display')
.controller('MetadataDisplayController', MetadataDisplayController);
HzMetadataDisplayController.$inject = [
'$scope',
'horizon.framework.widgets.metadata-tree.service',
'horizon.framework.widgets.metadata-display.defaults'
MetadataDisplayController.$inject = [
'horizon.framework.widgets.metadata.tree.service'
];
function HzMetadataDisplayController($scope, metadataTreeService, defaults) {
/**
* @ngdoc controller
* @name MetadataDisplayController
* @description
* Controller used by `metadataDisplay`
*/
function MetadataDisplayController(metadataTreeService) {
var ctrl = this;
ctrl.text = angular.extend({}, defaults.text, $scope.text);
ctrl.tree = null;
ctrl.selected = null;
ctrl.hide = true;
@ -55,7 +54,7 @@
init();
function init() {
ctrl.tree = new metadataTreeService.Tree($scope.available, $scope.existing);
ctrl.tree = new metadataTreeService.Tree(ctrl.available, ctrl.existing);
angular.forEach(ctrl.tree.flatTree, function (item) {
if (item.added) {
if (!item.leaf) {

View File

@ -1,4 +1,6 @@
/*
* Copyright 2015, Intel Corp.
*
* 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
@ -14,33 +16,36 @@
(function () {
'use strict';
angular
.module('horizon.framework.widgets.metadata.display')
.directive('metadataDisplay', metadataDisplay);
metadataDisplay.$inject = ['horizon.framework.widgets.metadata.display.basePath'];
/**
* @ngdoc directive
* @name horizon.framework.widgets.metadata-display.directive:hzMetadataDisplay
* @name metadataDisplay
* @restrict E
* @scope
*
* @description
* The `hzMetadataDisplay` displays existing metadata.
* The `metadataDisplay` displays existing metadata.
*
* @param {object[]} available List of available namespaces
* @param {object} existing Key-value pairs with existing properties
* @param {object=} text Text override
*/
angular
.module('horizon.framework.widgets.metadata-display')
.directive('hzMetadataDisplay', hzMetadataDisplay);
hzMetadataDisplay.$inject = ['horizon.framework.widgets.basePath'];
function hzMetadataDisplay(path) {
function metadataDisplay(path) {
var directive = {
controller: 'HzMetadataDisplayController as ctrl',
bindToController: true,
controller: 'MetadataDisplayController as ctrl',
restrict: 'E',
scope: {
available: '=',
existing: '=',
text: '=?'
},
templateUrl: path + 'metadata-display/metadata-display.html'
templateUrl: path + 'metadata-display.html'
};
return directive;

View File

@ -22,7 +22,7 @@
</div>
<div class="row description">
<div class="col-sm-12">
<strong><span ng-bind="::ctrl.text.detail"></span></strong>
<strong><span translate>Detail Information</span></strong>
</div>
<div class="col-sm-12">
<span ng-bind="ctrl.selected.description"></span>
@ -30,4 +30,4 @@
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,35 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
angular
.module('horizon.framework.widgets.metadata', [
'horizon.framework.widgets.metadata.tree',
'horizon.framework.widgets.metadata.display'
], config);
config.$inject = [
'$provide',
'$windowProvider'
];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'framework/widgets/metadata/';
$provide.constant('horizon.framework.widgets.metadata.basePath', path);
}
})();

View File

@ -0,0 +1,2 @@
@import "tree/tree";
@import "display/display";

View File

@ -0,0 +1,116 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
angular
.module('horizon.framework.widgets.metadata.tree')
.controller('MetadataTreeItemController', MetadataTreeItemController);
/**
* @ngdoc controller
* @name MetadataTreeItemController
* @description
* Controller used by `metadataTreeItem`
*/
function MetadataTreeItemController() {
var ctrl = this;
ctrl.formatErrorMessage = formatErrorMessage;
ctrl.opened = false;
if (ctrl.item.leaf.type === 'array') {
ctrl.values = ctrl.item.leaf.items.enum.filter(filter).sort();
if (!ctrl.item.leaf.readonly) {
ctrl.addValue = addValue;
ctrl.removeValue = removeValue;
ctrl.switchOpened = switchOpened;
ctrl.opened = ctrl.item.leaf.value.length === 0;
}
}
function formatErrorMessage(item, error) {
if (error.min) {
return ctrl.text.min + ' ' + item.leaf.minimum;
}
if (error.max) {
return ctrl.text.max + ' ' + item.leaf.maximum;
}
if (error.minlength) {
return ctrl.text.minLength + ' ' + item.leaf.minLength;
}
if (error.maxlength) {
return ctrl.text.maxLength + ' ' + item.leaf.maxLength;
}
if (error.pattern) {
if (item.leaf.type === 'integer') {
return ctrl.text.integerRequired;
} else {
return ctrl.text.patternMismatch;
}
}
if (error.number) {
if (item.leaf.type === 'integer') {
return ctrl.text.integerRequired;
} else {
return ctrl.text.decimalRequired;
}
}
if (error.required) {
return ctrl.text.required;
}
}
function filter (i) {
return ctrl.item.leaf.value.indexOf(i) < 0;
}
function remove(array, value) {
var index = array.indexOf(value);
if (index > -1) {
array.splice(index, 1);
}
return array;
}
function addValue(val) {
ctrl.item.leaf.value.push(val);
ctrl.item.leaf.value.sort();
remove(ctrl.values, val);
}
function removeValue(val) {
remove(ctrl.item.leaf.value, val);
ctrl.values.push(val);
ctrl.values.sort();
if (ctrl.item.leaf.value.length === 0) {
ctrl.opened = true;
}
}
function switchOpened() {
ctrl.opened = !ctrl.opened;
}
}
})();

View File

@ -0,0 +1,55 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
angular
.module('horizon.framework.widgets.metadata.tree')
.directive('metadataTreeItem', metadataTreeItem);
metadataTreeItem.$inject = ['horizon.framework.widgets.metadata.tree.basePath'];
/**
* @ngdoc directive
* @name metadataTreeItem
* @restrict E
* @scope
*
* @description
* The `metadataTreeItem` helper directive displays proper field for
* editing Item.leaf.value depending on Item.leaf.type
*
* @param {expression} action Action for button
* @param {Item} item Item to display
* @param {object} text Text override
*/
function metadataTreeItem(path) {
var directive = {
bindToController: true,
controller: 'MetadataTreeItemController as ctrl',
restrict: 'E',
scope: {
action: '&',
item: '=',
text: '='
},
templateUrl: path + 'metadata-tree-item.html'
};
return directive;
}
})();

View File

@ -0,0 +1,115 @@
<div class="metadata-tree-item" ng-form="itemForm">
<div class="input-group input-group-sm"
ng-switch on="ctrl.item.leaf.type"
ng-if="ctrl.item.leaf.type !== 'array'"
ng-class="{'has-error': itemForm.property.$invalid && itemForm.property.$dirty}">
<span class="input-group-addon"
title="{$ ::ctrl.item.leaf.name $}"
ng-bind="::ctrl.item.leaf.name">
</span>
<input ng-switch-when="string"
ng-if="!ctrl.item.leaf.enum"
name="property"
type="text"
class="form-control"
pattern="{$ ::ctrl.item.leaf.pattern $}"
required
ng-model="ctrl.item.leaf.value"
ng-minlength="{$ ::ctrl.item.leaf.minLength $}"
ng-maxlength="{$ ::ctrl.item.leaf.maxLength $}"
ng-disabled="ctrl.item.leaf.readonly"/>
<select ng-switch-when="string"
ng-if="ctrl.item.leaf.enum"
name="property"
class="form-control"
required
ng-model="ctrl.item.leaf.value"
ng-options="op for op in ctrl.item.leaf.enum"
ng-disabled="ctrl.item.leaf.readonly">
</select>
<input ng-switch-when="integer"
name="property"
type="number"
class="form-control"
required
ng-model="ctrl.item.leaf.value"
ng-pattern="/^-?\d+$/"
min="{$ ::ctrl.item.leaf.minimum $}"
max="{$ ::ctrl.item.leaf.maximum $}"
step="1"
ng-disabled="ctrl.item.leaf.readonly"/>
<input ng-switch-when="number"
name="property"
type="number"
class="form-control"
required
ng-model="ctrl.item.leaf.value"
min="{$ ::ctrl.item.leaf.minimum $}"
max="{$ ::ctrl.item.leaf.maximum $}"
ng-disabled="ctrl.item.leaf.readonly"/>
<div class="input-group-addon bool"
ng-switch-when="boolean">
<input name="property"
type="checkbox"
ng-model="ctrl.item.leaf.value"
ng-disabled="ctrl.item.leaf.readonly"/>
</div>
<div class="input-group-btn">
<a class="btn btn-default"
ng-click="ctrl.action()">
<span class="fa fa-minus"></span>
</a>
</div>
</div>
<div ng-if="ctrl.item.leaf.type === 'array'"
class="panel panel-default multiselect">
<div class="panel-heading">
<div ng-click="ctrl.switchOpened()">
<span class="fa fa-fw"
ng-class="ctrl.item.leaf.readonly ? '' : ctrl.opened ? 'fa-chevron-down' : 'fa-chevron-right'">
</span>
<span ng-bind="::ctrl.item.leaf.name"></span>
<div class="text-right">
<a class="btn btn-xs btn-default" ng-click="ctrl.action()">
<i class="fa fa-minus"></i>
</a>
</div>
</div>
</div>
<div class="panel-body values"
ng-class="{disabled: ctrl.item.leaf.readonly}">
<span ng-repeat="val in ctrl.item.leaf.value">
<span class="label label-default"
ng-click="ctrl.removeValue(val)">
<span ng-bind="::val"></span>
<span class="fa fa-times" ng-if="!ctrl.item.leaf.readonly"></span>
</span>
</span>
</div>
<ul class="list-group options" ng-show="ctrl.opened">
<li class="list-group-item"
ng-repeat="val in ctrl.values"
ng-click="ctrl.addValue(val)">
<span ng-bind="::val"></span>
</li>
</ul>
<div class="panel-footer" ng-show="ctrl.opened">
<div class="form-inline clearfix">
<div class="form-group pull-right">
<label>Operator</label>
<select class="form-control input-sm"
ng-model="ctrl.item.leaf.operator"
ng-options="val for val in ctrl.item.leaf.operators">
</select>
</div>
</div>
</div>
</div>
<div class="label label-info">
<span ng-bind="::ctrl.item.breadcrumb()"></span>
</div>
<div class="label label-danger"
ng-if="itemForm.$invalid && itemForm.$dirty">
<span ng-bind="ctrl.formatErrorMessage(ctrl.item, itemForm.property.$error)"></span>
</div>
</div>

View File

@ -0,0 +1,52 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
angular
.module('horizon.framework.widgets.metadata.tree')
.directive('metadataTreeUnique', metadataTreeUnique);
/**
* @ngdoc directive
* @name metadataTreeUnique
* @restrict A
*
* @description
* The `metadataTreeUnique` helper directive provides validation
* for field which value should be unique new Item
*/
function metadataTreeUnique() {
var directive = {
restrict: 'A',
require: ['^metadataTree', 'ngModel'],
link: link
};
return directive;
function link(scope, elm, attrs, controllers) {
var metadataTree = controllers[0];
var ngModel = controllers[1];
ngModel.$validators.unique = function (modelValue, viewValue) {
return !metadataTree.tree.flatTree.some(function (item) {
return item.leaf && item.leaf.name === viewValue;
});
};
}
}
})();

View File

@ -0,0 +1,54 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
angular
.module('horizon.framework.widgets.metadata.tree')
.controller('MetadataTreeController', MetadataTreeController);
MetadataTreeController.$inject = [
'horizon.framework.widgets.metadata.tree.service',
'horizon.framework.widgets.metadata.tree.defaults'
];
/**
* @ngdoc controller
* @name MetadataTreeController
* @description
* Controller used by `metadataTree`
*/
function MetadataTreeController(metadataTreeService, defaults) {
var ctrl = this;
ctrl.availableFilter = availableFilter;
ctrl.text = angular.extend({}, defaults.text, ctrl.text);
if (!ctrl.tree) {
ctrl.tree = new metadataTreeService.Tree(ctrl.available, ctrl.existing);
}
ctrl.customItem = '';
ctrl.filterText = {
available: '',
existing: ''
};
function availableFilter(item) {
return !item.added && (
ctrl.filterText.available.length === 0 ? item.visible : true);
}
}
})();

View File

@ -0,0 +1,59 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
angular
.module('horizon.framework.widgets.metadata.tree')
.directive('metadataTree', metadataTree);
metadataTree.$inject = ['horizon.framework.widgets.metadata.tree.basePath'];
/**
* @ngdoc directive
* @name metadataTree
* @restrict E
* @scope
*
* @description
* The `metadataTree` directive provide support for modifying existing
* metadata properties and adding new from metadata catalog.
*
* @param {Tree=} model Model binding
* @param {object[]=} available List of available namespaces
* @param {object=} existing Key-value pairs with existing properties
* @param {object=} text Text override
* @param {object=} form Existing items form binding
*/
function metadataTree(path) {
var directive = {
bindToController: true,
controller: 'MetadataTreeController as ctrl',
restrict: 'E',
scope: {
tree: '=*?model',
available: '=?',
existing: '=?',
text: '=?',
metadataForm: '=?form'
},
templateUrl: path + 'metadata-tree.html'
};
return directive;
}
})();

View File

@ -1,25 +1,25 @@
<div class="metadata-tree">
<div class="row">
<p class="col-md-12">
<span ng-bind="::text.help"></span>
<span ng-bind="::ctrl.text.help"></span>
</p>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel panel-default" ng-form="treeForm">
<div class="panel panel-default">
<div class="panel-heading">
<div class="row">
<div class="col-md-6 v-align">
<strong>
<span ng-bind="::text.available"></span>
<span ng-bind="::ctrl.text.available"></span>
</strong>
</div>
<div class="col-md-6">
<div class="has-feedback">
<input class="form-control input-sm"
type="text" placeholder="{$ ::text.filter $}"
ng-model="filterText.available"/>
type="text" placeholder="{$ ::ctrl.text.filter $}"
ng-model="ctrl.filterText.available"/>
<span class="fa fa-search form-control-feedback"></span>
</div>
</div>
@ -29,30 +29,30 @@
<li class="list-group-item" ng-form="customItemForm">
<div class="input-group input-group-sm"
ng-class="{'has-error': customItemForm.$invalid && customItemForm.$dirty}">
<span class="input-group-addon" ng-bind="::text.custom"></span>
<span class="input-group-addon" ng-bind="::ctrl.text.custom"></span>
<input class="form-control" type="text" name="customItem"
ng-model="customItem"
hz-metadata-tree-unique/>
ng-model="ctrl.customItem"
metadata-tree-unique/>
<span class="input-group-btn">
<button type='button' class="btn btn-primary"
ng-click="tree.addCustom(customItem)"
ng-disabled="!customItem || customItemForm.$invalid">
ng-click="ctrl.tree.addCustom(ctrl.customItem); ctrl.customItem=''"
ng-disabled="!ctrl.customItem || customItemForm.$invalid">
<span class="fa fa-plus"></span>
</button>
</span>
</div>
<div class="label label-danger"
ng-if="customItemForm.$invalid && customItemForm.$dirty">
<span ng-bind="::text.duplicate"></span>
<span ng-bind="::ctrl.text.duplicate"></span>
</div>
</li>
<li ng-repeat="item in availableList = (tree.flatTree | filter: {$: filterText.available} | filter: availableFilter)"
ng-class="'level-' + item.level + (tree.selected===item?' active':'')"
<li ng-repeat="item in availableList = (ctrl.tree.flatTree | filter: {$: ctrl.filterText.available} | filter: ctrl.availableFilter)"
ng-class="'level-' + item.level + (ctrl.tree.selected===item?' active':'')"
ng-class-odd="'dark-stripe'"
ng-class-even="'light-stripe'"
class="list-group-item"
ng-click="tree.select(item)">
ng-click="ctrl.tree.select(item)">
<div class="clearfix">
<div class="pull-left">
<span ng-class="{leaf: item.leaf}">
@ -63,7 +63,7 @@
</div>
<div class="pull-right">
<a class="btn btn-primary btn-xs"
ng-click="tree.markAsAdded(item)">
ng-click="ctrl.tree.markAsAdded(item)">
<span class="fa fa-plus"></span>
</a>
</div>
@ -71,7 +71,7 @@
</li>
<li class="list-group-item disabled"
ng-show="availableList.length===0">
<span ng-bind="::text.noAvailable"></span>
<span ng-bind="::ctrl.text.noAvailable"></span>
</li>
</ul>
</div>
@ -83,33 +83,33 @@
<div class="row">
<div class="col-md-6 v-align">
<strong>
<span ng-bind="::text.existing"></span>
<span ng-bind="::ctrl.text.existing"></span>
</strong>
</div>
<div class="col-md-6">
<div class="has-feedback">
<input class="form-control input-sm"
type="text" placeholder="{$ ::text.filter $}"
ng-model="filterText.existing"/>
type="text" placeholder="{$ ::ctrl.text.filter $}"
ng-model="ctrl.filterText.existing"/>
<span class="fa fa-search form-control-feedback"></span>
</div>
</div>
</div>
</div>
<ul class="list-group metadata-list-group" ng-cloak>
<li ng-repeat="item in existingList = (tree.flatTree | filter:{$:filterText.existing, added:true, leaf:'!null'} | orderBy:'leaf.name')"
ng-class="{'active': tree.selected===item}"
<ul class="list-group metadata-list-group" ng-form="ctrl.metadataForm" ng-cloak>
<li ng-repeat="item in existingList = (ctrl.tree.flatTree | filter:{$:ctrl.filterText.existing, added:true, leaf:'!null'} | orderBy:'leaf.name')"
ng-class="{'active': ctrl.tree.selected===item}"
ng-class-odd="'dark-stripe'"
ng-class-even="'light-stripe'"
class="list-group-item"
ng-click="tree.select(item)">
<hz-metadata-tree-item item="item" text="text"
action="tree.unmarkAsAdded(item)">
</hz-metadata-tree-item>
ng-click="ctrl.tree.select(item)">
<metadata-tree-item item="item" text="ctrl.text"
action="ctrl.tree.unmarkAsAdded(item)">
</metadata-tree-item>
</li>
<li class="list-group-item disabled"
ng-show="existingList.length===0">
<span ng-bind="::text.noExisting"></span>
<span ng-bind="::ctrl.text.noExisting"></span>
</li>
</ul>
</div>
@ -117,18 +117,18 @@
</div>
<div class="well">
<div ng-show="tree.selected">
<div ng-show="ctrl.tree.selected">
<p>
<strong>
<span ng-bind="tree.selected.label"></span>
<span ng-bind="ctrl.tree.selected.label"></span>
</strong>
<span ng-show="tree.selected.leaf">(<em><span ng-bind="tree.selected.leaf.name"></span></em>)</span>
<span ng-show="ctrl.tree.selected.leaf">(<em><span ng-bind="ctrl.tree.selected.leaf.name"></span></em>)</span>
</p>
<p><span ng-bind="tree.selected.description"></span></p>
<p><span ng-bind="ctrl.tree.selected.description"></span></p>
</div>
<div ng-hide="tree.selected">
<span ng-bind="::text.help"></span>
<div ng-hide="ctrl.tree.selected">
<span ng-bind="::ctrl.text.help"></span>
</div>
</div>
</div>

View File

@ -0,0 +1,72 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
/*eslint-disable max-len */
/**
* @ngdoc overview
* @name horizon.framework.widgets.metadata.tree
* @description
*
* # horizon.framework.widgets.metadata.tree
*
* The `horizon.framework.widgets.metadata.tree` provides widgets and service
* with logic for editing metadata.
*
* | Components |
* |--------------------------------------------------------------------------------------------|
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTree `metadataTree`} |
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTreeItem `metadataTreeItem`} |
* | {@link horizon.framework.widgets.metadata.tree.directive:metadataTreeUnique `metadataTreeUnique`} |
* | {@link horizon.framework.widgets.metadata.tree.controller:MetadataTreeController `MetadataTreeController`} |
* | {@link horizon.framework.widgets.metadata.tree.controller:MetadataTreeItemController `MetadataTreeItemController`} |
*
*/
/*eslint-enable max-len */
angular
.module('horizon.framework.widgets.metadata.tree', [], config)
.constant('horizon.framework.widgets.metadata.tree.defaults', {
text: {
/*eslint-disable max-len */
help: gettext('You can specify resource metadata by moving items from the left column to the right column. In the left columns there are metadata definitions from the Glance Metadata Catalog. Use the "Other" option to add metadata with the key of your choice.'),
/*eslint-enable max-len */
min: gettext('Min'),
max: gettext('Max'),
minLength: gettext('Min length'),
maxLength: gettext('Max length'),
patternMismatch: gettext('Pattern mismatch'),
integerRequired: gettext('Integer required'),
decimalRequired: gettext('Decimal required'),
required: gettext('Required'),
duplicate: gettext('Duplicate keys are not allowed'),
filter: gettext('Filter'),
available: gettext('Available Metadata'),
existing: gettext('Existing Metadata'),
custom: gettext('Custom'),
noAvailable: gettext('No available metadata'),
noExisting: gettext('No existing metadata')
}
});
config.$inject = ['$provide', '$windowProvider'];
function config($provide, $windowProvider) {
var path = $windowProvider.$get().STATIC_URL + 'framework/widgets/metadata/tree/';
$provide.constant('horizon.framework.widgets.metadata.tree.basePath', path);
}
})();

View File

@ -1,37 +1,35 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
angular.module('horizon.framework.widgets.metadata-tree')
angular
.module('horizon.framework.widgets.metadata.tree')
.factory('horizon.framework.widgets.metadata.tree.service', metadataTreeService);
/**
* @ngdoc service
* @name horizon.framework.widgets.metadata-tree.metadataTreeService
* @name metadataTreeService
*/
.factory('horizon.framework.widgets.metadata-tree.service', [ function () {
/**
* Parse value into boolean
*
* @param {(string|boolean)} value
* @returns {boolean}
*/
function parseBool(value) {
var valueType = typeof(value);
if (valueType === 'boolean') {
return value;
} else if (valueType === 'string') {
value = value.toLowerCase();
if (value === 'true') {
return true;
} else if (value === 'false') {
return false;
}
}
return null;
}
function metadataTreeService() {
var service = {
Item: Item,
Property: Property,
Tree: Tree
};
/**
* Construct a new property
@ -76,28 +74,36 @@
}
switch (this.type) {
case 'integer': this.value = parseInt(value); break;
case 'number': this.value = parseFloat(value); break;
case 'integer':
this.value = parseInt(value);
break;
case 'number':
this.value = parseFloat(value);
break;
case 'array':
var data = /^(<.*?>) (.*)$/.exec(value);
if (data) {
this.operator = data[1];
this.value = data[2].split(',');
} break;
case 'boolean': this.value = parseBool(value); break;
default: this.value = value;
}
break;
case 'boolean':
this.value = parseBool(value);
break;
default:
this.value = value;
}
};
/**
* Serialize {@link Property#value} and returns it
*
* @returns {*}
* @returns {string}
*/
Property.prototype.getValue = function () {
switch (this.type) {
case 'array': return this.operator + ' ' + this.value.join(',');
default: return this.value;
default: return '' + this.value;
}
};
@ -105,7 +111,7 @@
* Construct a new tree node
*
* @class Item
* @param {Item} parent
* @param {Item=} parent
*
* @property {Item} parent Item parent
* @property {Item[]} children Item children
@ -114,7 +120,7 @@
function Item(parent) {
// parent as property to prevent infinite recursion in angular filter
Object.defineProperty(this, 'parent', {
value: typeof parent !== 'undefined' ? parent : null
value: parent || null
});
this.children = [];
// Node properties
@ -197,7 +203,7 @@
* Load Item values from property definition and mark as custom
*
* @param {string} name Property name
* @param {object} property Metadata property definition
* @param {object=} property Metadata property definition
* @returns {Item}
*/
Item.prototype.customProperty = function (name, property) {
@ -210,7 +216,7 @@
/**
* Expand Item by marking all children as visible
*
* @param {boolean} deep Whether to recursively expand all child Items
* @param {boolean=} deep Whether to recursively expand all child Items
*/
Item.prototype.expand = function (deep) {
this.expanded = true;
@ -487,10 +493,28 @@
this.selected = item;
};
return {
Item: Item,
Property: Property,
Tree: Tree
};
}]);
/**
* Parse value into boolean
*
* @param {(string|boolean)} value
* @returns {boolean}
*/
function parseBool(value) {
if (value === true || value === false) {
return value;
} else if (angular.isString(value)) {
value = value.toLowerCase();
if (value === 'true') {
return true;
} else if (value === 'false') {
return false;
}
}
return null;
}
return service;
}
})();

View File

@ -1,9 +1,24 @@
/*
* Copyright 2015, Intel Corp.
*
* 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.
*/
(function () {
'use strict';
describe('horizon.framework.widgets.metadata-tree module', function () {
describe('horizon.framework.widgets.metadata.tree module', function () {
it('should have been defined', function () {
expect(angular.module('horizon.framework.widgets.metadata-tree')).toBeDefined();
expect(angular.module('horizon.framework.widgets.metadata.tree')).toBeDefined();
});
var namespaces = [
@ -89,9 +104,10 @@
beforeEach(module('templates'));
beforeEach(module('horizon.framework'));
beforeEach(module('horizon.framework.widgets'));
beforeEach(module('horizon.framework.widgets.metadata-tree'));
beforeEach(module('horizon.framework.widgets.metadata'));
beforeEach(module('horizon.framework.widgets.metadata.tree'));
describe('hzMetadataTree directive', function () {
describe('metadataTree directive', function () {
beforeEach(inject(function ($injector) {
var $compile = $injector.get('$compile');
@ -100,14 +116,14 @@
$scope.available = namespaces;
$scope.existing = { 'test:B:A:1':'foo' };
var markup = '<hz-metadata-tree' +
var markup = '<metadata-tree' +
' available="available"' +
' existing="existing">' +
'</hz-metadata-tree>';
'</metadata-tree>';
$element = angular.element(markup);
$compile($element)($scope);
$scope.$digest();
$scope.$apply();
}));
it('should have 2 rows in available list', function () {
@ -161,31 +177,31 @@
});
});
describe('hzMetadataTreeItem directive', function () {
describe('metadataTreeItem directive', function () {
var $scope, $element, item;
beforeEach(inject(function ($injector) {
var $compile = $injector.get('$compile');
$scope = $injector.get('$rootScope').$new();
var serviceName = 'horizon.framework.widgets.metadata-tree.service';
var serviceName = 'horizon.framework.widgets.metadata.tree.service';
item = new ($injector.get(serviceName).Item)();
$scope.item = item.fromProperty('test', namespaces[0].properties['test:A:6']);
var markup = '<hz-metadata-tree-item' +
var markup = '<metadata-tree-item' +
' item="item" text="text" action="">' +
'</hz-metadata-tree-item>';
'</metadata-tree-item>';
$element = angular.element(markup);
$compile($element)($scope);
$scope.$digest();
$scope.$apply();
}));
it('should have additional methods for array ', function () {
expect($element.isolateScope().opened).toBe(false);
expect($element.isolateScope().add).toBeDefined();
expect($element.isolateScope().remove).toBeDefined();
expect($element.isolateScope().open).toBeDefined();
expect($element.isolateScope().ctrl.opened).toBe(false);
expect($element.isolateScope().ctrl.addValue).toBeDefined();
expect($element.isolateScope().ctrl.removeValue).toBeDefined();
expect($element.isolateScope().ctrl.switchOpened).toBeDefined();
});
it('should add values to array ', function () {

View File

@ -12,8 +12,7 @@
'horizon.framework.widgets.transfer-table',
'horizon.framework.widgets.charts',
'horizon.framework.widgets.action-list',
'horizon.framework.widgets.metadata-tree',
'horizon.framework.widgets.metadata-display',
'horizon.framework.widgets.metadata',
'horizon.framework.widgets.toast',
'MagicSearch'
])

View File

@ -7,6 +7,5 @@
@import "charts/pie-chart";
@import "action-list/action-list";
@import "modal-wait-spinner/modal-wait-spinner";
@import "metadata-tree/metadata-tree";
@import "metadata-display/metadata-display";
@import "metadata/metadata";
@import "magic-search/magic-search";

View File

@ -7,9 +7,9 @@
{% block ng_controller %}hzModalFormUpdateMetadataCtrl{% endblock %}
{% block modal-body %}
<hz-metadata-tree available="available"
<metadata-tree available="available"
existing="existing"
model="tree"></hz-metadata-tree>
model="tree"></metadata-tree>
<script type="text/javascript">
var existing_metadata = JSON.parse('{{ existing_metadata|escapejs }}');
var available_metadata = JSON.parse('{{ available_metadata|escapejs }}');

View File

@ -110,10 +110,10 @@ limitations under the License.
<hr>
<div class="row" ng-if="item.extras">
<div class="col-sm-12">
<hz-metadata-display
<metadata-display
available="::metadataDefs.flavor"
existing="::item.extras">
</hz-metadata-display>
</metadata-display>
</div>
</div>
</div>

View File

@ -22,17 +22,17 @@
<div class="col-sm-12">
<div ng-if="row.properties && model.metadataDefs.image">
<hz-metadata-display
<metadata-display
available="::model.metadataDefs.image"
existing="::row.properties">
</hz-metadata-display>
</metadata-display>
</div>
<div ng-if="row.volume_image_metadata && model.metadataDefs.volume">
<hz-metadata-display
<metadata-display
available="::model.metadataDefs.volume"
existing="::row.volume_image_metadata">
</hz-metadata-display>
</metadata-display>
</div>
</div>