Merge "Modify hz-cell to use hz-field"

This commit is contained in:
Jenkins 2016-07-06 20:33:37 +00:00 committed by Gerrit Code Review
commit 8850cdda24
8 changed files with 244 additions and 32 deletions

View File

@ -0,0 +1,131 @@
/**
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* 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.property')
.directive('hzField', hzField);
hzField.$inject = ['$filter'];
/**
* @ngdoc directive
* @name hzField
* @param config {Object} - The field definition object, described below
* @param item {Object} - The object containing the property from config.id
* @description
* The `hzField` directive allows you to output an object's property using
* formatting as provided by a field configuration.
*
* The config object describes a single field, and the config object's 'id'
* property matches the name of a property in the 'item' parameter. For
* example, if config.id is 'name' then there should be an item.name that
* is evaluated for display using the logic described below.
*
* The field configuration may transform the data in the item's property
* using either a set of single-argument filters or functions, specified by
* the 'filters' property, or using the 'values' object in which the item
* property is mapped via the keys to the values in the given object. Note
* that a combination of 'filters' and 'values' may be used; in this case
* the filters are evaluated first. This allows for translations that will
* map to keys first (e.g. upper-casing a string with a filter so it matches
* upper-case keys), and allows the values provided in the 'values' mapping
* to be the final value produced. The 'urlFunction' option allows for a
* a function to be given, where the item is the sole parameter and the result
* should be a URL.
*
* @restrict E
*
* @scope
* @example
*
* var config = {id: 'a', title: 'Header A', priority: 1};
*
* // Using urlFunction to create a link
* var linked = {id: 'b', title: 'Header B', priority: 2, urlFunction: myUrlFunction},
*
* // Using defaultSort
* var defaultSort = {id: 'c', title: 'Header C', priority: 1, sortDefault: true};
*
* // Using filters (can be combined with 'values')
* var filtered = {id: 'd', title: 'Header D', priority: 2,
* filters: [someFilterFunction, 'uppercase']};
*
* // Using value mappings
* var mapped = {id: 'e', title: 'Header E', priority: 1,
* values: {
* 'a': 'apple',
* 'j': 'jacks'
* }
* };
*
* function myUrlFunction(item) {
* return '/my/path/' + item.id;
* }
*
* ```
* <hz-field config="config" item="item"></hz-field>
* ```
*
*/
function hzField($filter) {
var directive = {
restrict: 'E',
scope: {
config: "=",
item: "="
},
link: link
};
return directive;
///////////////////
function link(scope, element) {
var config = scope.config;
var item = scope.item;
var propValue = item[config.id];
var output = propValue;
if (config && config.filters) {
for (var i = 0; i < config.filters.length; i++) {
var filter = config.filters[i];
// call horizon framework filter function if provided
if (angular.isFunction(filter)) {
output = filter(propValue);
// call angular filters
} else {
output = $filter(filter)(propValue);
}
}
}
if (config && config.values) {
// apply mapping values to the data if applicable
output = config.values[output];
}
var url;
if (config && config.urlFunction) {
url = config.urlFunction(item);
}
if (url) {
element.append(angular.element('<a>').attr('href', url).append(output));
} else {
element.append(output);
}
}
}
})();

View File

@ -0,0 +1,28 @@
/*
* (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP
*
* 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.property
* @description
* This module provides support for displaying properties of registered resource
* types.
*/
angular.module('horizon.framework.widgets.property', []);
})();

View File

@ -20,26 +20,29 @@
.module('horizon.framework.widgets.table')
.directive('hzCell', hzCell);
hzCell.$inject = ['$compile', '$filter'];
hzCell.$inject = ['$compile'];
/**
* @ngdoc directive
* @name horizon.framework.widgets.table.directive:hzCell
* @param table {Object} - The table/controller context
* @param column {Object} - The column definition object, described below
* @param item {Object} - The object containing the property from column.id
* @description
* The `hzCell` directive allows you to customize your cell content.
* When specifying your table configuration object, you may pass in a
* template per each column.
*
* See the documentation on hz-field for details on how to specify formatting
* based on the column configuration.
*
* You should define a template when you want to format data or show more
* complex content (e.g conditionally show different icons or a link).
* You should reference the cell's 'item' attribute in the template if
* you need access to the cell's data. The attributes 'column' and 'item'
* should be defined outside of this directive. See example below.
* you need access to the cell's data. See example below.
*
* It should ideally be used within the context of the `hz-dynamic-table` directive.
* The params passed into `hz-dynamic-table` can be used in the custom template,
* including the 'table' scope. 'table' can be referenced if you want to pass in an
* outside scope.
* 'table' can be referenced in a template if you want to pass in an outside scope.
*
* @restrict E
*
@ -55,27 +58,37 @@
* {id: 'c', title: 'Header C', priority: 1, sortDefault: true},
* {id: 'd', title: 'Header D', priority: 2,
* template: '<span class="fa fa-bolt">{$ item.id $}</span>',
* filters: [someFilterFunction, 'uppercase']}
* filters: [someFilterFunction, 'uppercase']},
* {id: 'e', title: 'Header E', priority: 1,
* values: {
* 'a': 'apple',
* 'j': 'jacks'
* }
* }
* ]
* };
*
* ```
* <tbody>
* <tbody ng-controller="TableCtrl as table">
* <tr ng-repeat="item in items track by $index">
* <td ng-repeat="column in config.columns"
* class="{$ column.classes $}">
* <hz-cell></hz-cell>
* <hz-cell table="table" column="column" item="item"></hz-cell>
* </td>
* </tr>
* </tbody>
* ```
*
*/
function hzCell($compile, $filter) {
function hzCell($compile) {
var directive = {
restrict: 'E',
scope: false,
scope: {
table: '=',
column: '=',
item: '='
},
link: link
};
return directive;
@ -84,26 +97,14 @@
function link(scope, element) {
var column = scope.column;
var item = scope.item;
var html;
// if template provided, render, and place into cell
if (column && column.template) {
// if template provided, render, and place into cell
html = $compile(column.template)(scope);
} else {
// apply filters to cell data if applicable
html = item[column.id];
if (column && column.filters) {
for (var i = 0; i < column.filters.length; i++) {
var filter = column.filters[i];
// call horizon framework filter function if provided
if (angular.isFunction(filter)) {
html = filter(item[column.id]);
// call angular filters
} else {
html = $filter(filter)(item[column.id]);
}
}
}
// NOTE: 'table' is not passed to hz-field as hz-field is intentionally
// not cognizant of a 'table' context as hz-cell is.
html = $compile('<hz-field config="column" item="item"></hz-field>')(scope);
}
element.append(html);
}

View File

@ -12,7 +12,7 @@
<span class="rsp-alt-p2">
<dl class="col-sm-2" ng-repeat="column in config.columns">
<dt translate>{$ column.title $}</dt>
<dd translate><hz-cell></hz-cell></dd>
<dd translate><hz-cell table="table" column="column" item="item"></hz-cell></dd>
</dl>
</span>
</div>
</div>

View File

@ -63,7 +63,7 @@
</td>
<td ng-repeat="column in config.columns"
class="rsp-p{$ column.priority $}">
<hz-cell></hz-cell>
<hz-cell table="table" column="column" item="item"></hz-cell>
</td>
<td ng-if="itemActions" class="actions_column">
<!--

View File

@ -222,6 +222,54 @@
expect($element.find('tbody tr:eq(1) td:eq(3)').text()).toContain('reptile-ish');
expect($element.find('tbody tr:eq(2) td:eq(3)').text()).toContain('bird-ish');
});
it('properly maps the cell content given a mapping', function() {
$scope.config = {
selectAll: true,
expand: false,
trackId: 'id',
columns: [
{id: 'animal', title: 'Animal', priority: 1,
values: {
cat: "Catamount",
snake: "Serpent",
sparrow: "CAPTAIN Jack Sparrow"
}
},
{id: 'type', title: 'Type', priority: 2},
{id: 'diet', title: 'Diet', priority: 1, sortDefault: true},
{id: 'domestic', title: 'Domestic', priority: 2}
]
};
var $element = digestMarkup($scope, $compile, markup);
expect($element.find('tbody tr:eq(0) td:eq(2)').text()).toContain('Catamount');
expect($element.find('tbody tr:eq(1) td:eq(2)').text()).toContain('Serpent');
expect($element.find('tbody tr:eq(2) td:eq(2)').text()).toContain('CAPTAIN Jack Sparrow');
});
it('properly adds a link with urlFunction', function() {
$scope.config = {
selectAll: true,
expand: false,
trackId: 'id',
columns: [
{id: 'animal', title: 'Animal', priority: 1,
urlFunction: myFunction
},
{id: 'type', title: 'Type', priority: 2},
{id: 'diet', title: 'Diet', priority: 1, sortDefault: true},
{id: 'domestic', title: 'Domestic', priority: 2}
]
};
var $element = digestMarkup($scope, $compile, markup);
expect($element.find('tbody tr:eq(0) td:eq(2) a').attr('href')).toBe('/here/cat');
expect($element.find('tbody tr:eq(1) td:eq(2) a').attr('href')).toBe('/here/snake');
expect($element.find('tbody tr:eq(2) td:eq(2) a').attr('href')).toBe('/here/sparrow');
function myFunction(item) {
return '/here/' + item.animal;
}
});
});
});

View File

@ -23,6 +23,7 @@
'horizon.framework.widgets.details',
'horizon.framework.widgets.help-panel',
'horizon.framework.widgets.wizard',
'horizon.framework.widgets.property',
'horizon.framework.widgets.table',
'horizon.framework.widgets.modal',
'horizon.framework.widgets.modal-wait-spinner',

View File

@ -122,8 +122,7 @@
id: 'name',
priority: 1,
sortDefault: true,
template: '<a ng-href="{$ \'project/ngdetails/OS::Glance::Image/\' + item.id $}">' +
'{$ item.name $}</a>'
urlFunction: urlFunction
})
.append({
id: 'type',
@ -228,6 +227,10 @@
}
}
}
function urlFunction(item) {
return 'project/ngdetails/OS::Glance::Image/' + item.id;
}
}
/**