Merge "Metadata display widget"

This commit is contained in:
Jenkins 2015-03-18 23:04:46 +00:00 committed by Gerrit Code Review
commit 555d9e717a
10 changed files with 334 additions and 1 deletions

View File

@ -0,0 +1,33 @@
<div class="metadata-display" ng-if="!hide">
<div class="row">
<div class="col-sm-4 selector">
<div class="selector-item"
ng-repeat="item in tree.flatTree | filter: listFilter"
ng-click="onSelect(item)"
ng-class="{active: selected===item}">
<span ng-bind="::item.label"></span>
</div>
</div>
<div class="col-sm-8">
<div class="row">
<div ng-repeat="item in tree.flatTree | filter: childrenFilter">
<div class="col-sm-3 auto-width" title="{$ ::item.description $}">
<strong class="text-capitalize">
<span ng-bind="::item.label"></span>
</strong>
<span ng-bind="::item.leaf.value"></span>
</div>
</div>
</div>
<div class="row description">
<div class="col-sm-12">
<strong><span ng-bind="::text.detail"></span></strong>
</div>
<div class="col-sm-12">
<span ng-bind="selected.description"></span>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,122 @@
(function () {
'use strict';
/**
* @ngdoc overview
* @name hz.widget.metadata-display
* @description
*
* # hz.widget.metadata-display
*
* The `hz.widget.metadata-display` provides widget displaying metadata.
*
* | Directives |
* |---------------------------------------------------------------------------------------------|
* | {@link hz.widget.metadata-display.directive:hzMetadataDisplay `hzMetadataDisplay`} |
* |---------------------------------------------------------------------------------------------|
* | Controllers |
* |---------------------------------------------------------------------------------------------|
* | {@link hz.widget.metadata-display.controller:hzMetadataDisplayCtrl `hzMetadataDisplayCtrl`} |
*
*/
angular.module('hz.widget.metadata-display', [
'hz.widget.metadata-tree'
])
/**
* @ngdoc parameters
* @name hz.widget.metadata-display:metadataTreeDefaults
* @param {object} text Text constants
*/
.constant('metadataDisplayDefaults', {
text: {
detail: gettext('Detail Information')
}
})
/**
* @ngdoc directive
* @name hz.widget.metadata-display.directive:hzMetadataDisplay
* @scope
*
* @description
* The `hzMetadataDisplay` displays existing metadata.
*
* @param {object[]} available List of available namespaces
* @param {object} existing Key-value pairs with existing properties
* @param {object=} text Text override
*/
.directive('hzMetadataDisplay', ['basePath',
function (path) {
return {
scope: {
available: '=',
existing: '=',
text: '=?'
},
controller: 'hzMetadataDisplayCtrl',
templateUrl: path + 'metadata-display/metadata-display.html'
};
}
])
/**
* @ngdoc controller
* @name hz.widget.metadata-display.controller:hzMetadataDisplayCtrl
* @description
* Controller used by `hzMetadataDisplay`
*/
.controller('hzMetadataDisplayCtrl', [
'$scope', 'metadataTreeService', 'metadataDisplayDefaults',
function ($scope, metadataTreeService, defaults) {
function init() {
$scope.tree = new metadataTreeService.Tree($scope.available, $scope.existing);
angular.forEach($scope.tree.flatTree, function (item) {
if(item.added) {
if(!item.leaf) {
item.added = false;
if (item.parent) {
item.parent.addedCount -= 1;
}
}
else if(!item.custom) {
$scope.hide = false;
}
}
});
// select first item
$scope.tree.tree.some(function (item) {
if($scope.listFilter(item)) {
$scope.selected = item;
item.expand(true);
return true; // break
}
});
}
$scope.onSelect = function (item) {
$scope.selected.collapse();
item.expand(true);
$scope.selected = item;
};
$scope.childrenFilter = function (item) {
return item.visible && item.leaf && item.added;
};
$scope.listFilter = function (item) {
return item.addedCount > 0;
};
$scope.text = angular.extend({}, defaults.text, $scope.text);
$scope.tree = null;
$scope.selected = null;
$scope.hide = true;
init();
}
]);
}());

View File

@ -0,0 +1,29 @@
.metadata-display {
.selector {
.selector-item {
border-top: 1px solid $metadata-display-separator-color;
padding: 10px;
color: $metadata-display-selector-color;
&:first-child {
border-top: none;
}
&:hover {
color: $metadata-display-selector-hover-color;
}
&.active {
color: $metadata-display-selector-active-color;
}
}
}
.description {
margin-top: 20px;
}
.auto-width {
width: auto;
}
}

View File

@ -0,0 +1,135 @@
/* jshint globalstrict: true */
'use strict';
describe('hz.widget.metadata-display module', function() {
it('should have been defined', function () {
expect(angular.module('hz.widget.metadata-display')).toBeDefined();
});
var namespaces = [
{
"display_name": "Test Namespace A",
"description": "Test namespace description",
"properties": {
"test:A:1": {
"title": "Test A.1 - string",
"type": "string",
"default": "foo",
"enum": [
"option-1", "option-2", "option-3"
]
},
"test:A:2": {
"title": "Test A.2 - integer",
"type": "integer",
"default": "1",
"minimum": 0,
"maximum": 10
},
"test:A:3": {
"title": "Test A.3 - number",
"type": "number",
"default": "1.1",
"minimum": 0,
"maximum": 10
},
"test:A:4": {
"title": "Test A.4 - boolean",
"type": "boolean",
"default": "True"
},
"test:A:5": {
"title": "Test A.5 - boolean",
"type": "boolean",
"default": "false"
},
"test:A:6": {
"title": "Test A.6 - array",
"type": "array",
"items": {
"type": "string",
"enum": [
"val-1", "val-2", "val-3"
]
}
}
}
},
{
"display_name": "Test Namespace B",
"description": "Test namespace description",
"objects": [
{
"name": "Test Object A",
"description": "Test object description",
"properties": {
"test:B:A:1": {
"title": "Test B.A.1",
"description": "Test description"
},
"test:B:A:2": {}
}
},
{
"name": "Test Object B",
"description": "Test object description",
"properties": {
"test:B:B:1": {},
"test:B:B:2": {}
}
}
]
}
];
var existing = {
'test:A:1': 'option-2',
'test:A:2': '5',
'test:B:A:1': 'foo',
'test:B:B:1': 'bar'
};
describe('hzMetadataDisplay directive', function () {
var $scope, $element;
beforeEach(module('templates'));
beforeEach(module('hz'));
beforeEach(module('hz.widgets'));
beforeEach(module('hz.widget.metadata-tree'));
beforeEach(module('hz.widget.metadata-display'));
beforeEach(inject(function ($injector) {
var $compile = $injector.get('$compile');
$scope = $injector.get('$rootScope').$new();
$scope.available = namespaces;
$scope.existing = existing;
var markup =
'<hz-metadata-display available="available" existing="existing"></hz-metadata-display>';
$element = angular.element(markup);
$compile($element)($scope);
$scope.$digest();
}));
it('should have 3 rows in selector list', function() {
expect($element.find('.selector .selector-item').length).toBe(3);
});
it('should have 2 items in first group', function() {
expect($element.find('div[ng-repeat] div.auto-width').length).toBe(2);
});
it('should have 1 item in second group', function() {
$element.find('.selector .selector-item:nth-child(2)').trigger('click');
expect($element.find('div[ng-repeat] div.auto-width').length).toBe(1);
});
it('should have proper description', function() {
expect($element.find('span[ng-bind="selected.description"]').text()).toBe(namespaces[0].description);
$element.find('.selector .selector-item:nth-child(2)').trigger('click');
expect($element.find('span[ng-bind="selected.description"]').text()).toBe(namespaces[1].objects[0].description);
});
});
});

View File

@ -7,3 +7,4 @@
@import "action-list/action-list";
@import "modal-wait-spinner/modal-wait-spinner";
@import "metadata-tree/metadata-tree";
@import "metadata-display/metadata-display";

View File

@ -12,7 +12,8 @@
'hz.widget.transfer-table',
'hz.widget.charts',
'hz.widget.action-list',
'hz.widget.metadata-tree'
'hz.widget.metadata-tree',
'hz.widget.metadata-display'
])
.constant('basePath', '/static/angular/');

View File

@ -52,6 +52,7 @@
<script src='{{ STATIC_URL }}angular/charts/pie-chart.js'></script>
<script src='{{ STATIC_URL }}angular/metadata-tree/metadata-tree.js'></script>
<script src='{{ STATIC_URL }}angular/metadata-tree/metadata-tree-service.js'></script>
<script src='{{ STATIC_URL }}angular/metadata-display/metadata-display.js'></script>
<script src='{{ STATIC_URL }}horizon/lib/jquery/jquery.quicksearch.js'></script>
<script src="{{ STATIC_URL }}horizon/lib/jquery/jquery.tablesorter.js"></script>

View File

@ -10,6 +10,8 @@
<hz-metadata-tree available="available"
existing="existing"
model="tree"></hz-metadata-tree>
<hz-metadata-display available=""
existing="existing"></hz-metadata-display>
<script type="text/javascript">
var existing_metadata = {{ existing_metadata|safe }};
var available_metadata = {{ available_metadata|safe }};

View File

@ -39,6 +39,7 @@ class ServicesTests(test.JasmineTests):
'angular/table/basic-table.js',
'angular/transfer-table/transfer-table.js',
'angular/wizard/wizard.js',
'angular/metadata-display/metadata-display.js',
'horizon/js/angular/filters/filters.js',
]
specs = [
@ -55,6 +56,7 @@ class ServicesTests(test.JasmineTests):
'angular/transfer-table/transfer-table.spec.js',
'angular/wizard/wizard.spec.js',
'angular/metadata-tree/metadata-tree.spec.js',
'angular/metadata-display/metadata-display.spec.js',
'horizon/js/angular/filters/filters.spec.js',
]
externalTemplates = [
@ -71,4 +73,5 @@ class ServicesTests(test.JasmineTests):
'angular/wizard/wizard.html',
'angular/metadata-tree/metadata-tree.html',
'angular/metadata-tree/metadata-tree-item.html',
'angular/metadata-display/metadata-display.html',
]

View File

@ -176,3 +176,9 @@ $chart-title-padding: 0.5em 0 !default;
$chart-title-weight: 600 !default;
$chart-legend-font-size: 1em !default;
$chart-legend-padding: 0.2em 1.5em !default;
/* Metadata Display */
$metadata-display-selector-color: $link-color !default;
$metadata-display-selector-hover-color: $link-hover-color !default;
$metadata-display-selector-active-color: #666 !default;
$metadata-display-separator-color: #ccc !default;