Merge "Improvements to hz-dynamic-table"
This commit is contained in:
commit
377da87e3c
@ -38,7 +38,8 @@
|
||||
*
|
||||
* 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.
|
||||
* including the 'table' scope. 'table' can be referenced if you want to pass in an
|
||||
* outside scope.
|
||||
*
|
||||
* @restrict E
|
||||
*
|
||||
|
@ -29,24 +29,35 @@
|
||||
*
|
||||
* @param {object} config column definition used to generate the table (required)
|
||||
* @param {object} items original collection, passed into 'st-safe-src' attribute (required)
|
||||
* @param {object=} table any additional information that are
|
||||
* passed down to child widgets (e.g hz-cell) (optional)
|
||||
* @param {object=} batchActions batch actions for the table (optional)
|
||||
* @param {object=} itemActions item actions for each item/row (optional)
|
||||
* @param {object=} filterFacets Facets allowed for searching, if not provided,
|
||||
* default to simple text search (optional)
|
||||
* @param {object=} table is the name of a controller that should be passed
|
||||
* down to child widgets (e.g hz-cell) for additional attribute access (optional)
|
||||
* @param {object=} batchActions batch action-list actions for the table (optional)
|
||||
* @param {object=} itemActions item action-list actions for each item/row (optional)
|
||||
* @param {object=} filterFacets Facets used by hz-magic-search-context allowed for
|
||||
* searching. Filter will not be shown if this is not supplied (optional)
|
||||
* @param {function=} resultHandler function that is called with return value
|
||||
* from a clicked actions perform function passed into `actions` directive (optional)
|
||||
*
|
||||
* @description
|
||||
* The `hzDynamicTable` directive generates all the HTML content for a table.
|
||||
* You will need to pass in two attributes: `config` and `items`.
|
||||
* This directive is built off the Smart-table module, so `items`
|
||||
* is passed into `st-table` attribute.
|
||||
*
|
||||
* This directive is built off the Smart-table module, so `items` is passed into
|
||||
* `st-safe-src`.
|
||||
* Note: `st-safe-src' is used for async data, to keep track of modifications to the
|
||||
* original collection. Also, 'name' is the key used to retrieve cell data from base
|
||||
* 'displayedCollection'.
|
||||
* You can pass the following into `config` object:
|
||||
* selectAll {boolean} set to true if you want to enable select all checkbox
|
||||
* expand {boolean} set to true if you want to inline details
|
||||
* trackId {string} passed into ngRepeat's track by to identify objects
|
||||
* searchColumnSpan {number} is used to define the number of bootstrap grid columns the
|
||||
* search box will occupy. If this is set to 12 (the default) then the search box
|
||||
* and batch action buttons will be on separate rows.
|
||||
* actionColumnSpan {number} is the number of bootstrap grid columns the action buttons
|
||||
* should occupy. This defaults to 12, or the remainder of the row if searchColumnSpan
|
||||
* is less than 12 columns.
|
||||
* columns {Array} of objects to describe each column. Each object
|
||||
* requires: 'id', 'title', 'priority' (responsive priority when table resized)
|
||||
* optional: 'sortDefault', 'filters' (to apply to the column cells),
|
||||
* 'template' (see hz-cell directive for details)
|
||||
*
|
||||
* @example
|
||||
*
|
||||
@ -54,6 +65,7 @@
|
||||
* selectAll: true,
|
||||
* expand: true,
|
||||
* trackId: 'id',
|
||||
* searchColumnSpan: 6,
|
||||
* columns: [
|
||||
* {id: 'a', title: 'A', priority: 1},
|
||||
* {id: 'b', title: 'B', priority: 2},
|
||||
@ -71,16 +83,21 @@
|
||||
* config='config'
|
||||
* items="items"
|
||||
* table="table"
|
||||
* batchActions="batchActions"
|
||||
* itemActions="itemActions"
|
||||
* filterFacets="filterFacets"
|
||||
* resultHandler="resultHandler">
|
||||
* batch-actions="batchActions"
|
||||
* item-actions="itemActions"
|
||||
* filter-facets="filterFacets"
|
||||
* result-handler="resultHandler">
|
||||
* </hz-dynamic-table>
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
function hzDynamicTable(basePath) {
|
||||
|
||||
// <r1chardj0n3s>: there are some configuration items which are on the directive,
|
||||
// and some on the "config" attribute of the directive. Those latter configuration
|
||||
// items will be effectively "static" for the lifespan of the directive whereas
|
||||
// angular will watch directive attributes for changes. This should be revisited
|
||||
// at some point to make sure the split we've actually got here makes sense.
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
@ -109,6 +126,16 @@
|
||||
if (angular.isUndefined(scope.config.expand)) {
|
||||
scope.config.expand = true;
|
||||
}
|
||||
if (angular.isUndefined(scope.config.searchColumnSpan)) {
|
||||
scope.config.searchColumnSpan = 12;
|
||||
}
|
||||
if (angular.isUndefined(scope.config.actionColumnSpan)) {
|
||||
if (scope.config.searchColumnSpan < 12) {
|
||||
scope.config.actionColumnSpan = 12 - scope.config.searchColumnSpan;
|
||||
} else {
|
||||
scope.config.actionColumnSpan = 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
@ -2,15 +2,23 @@
|
||||
Dynamic table template
|
||||
-->
|
||||
<hz-magic-search-context filter-facets="filterFacets">
|
||||
<hz-magic-search-bar ng-if="filterFacets">
|
||||
</hz-magic-search-bar>
|
||||
<actions ng-if="filterFacets && batchActions" allowed="batchActions" type="batch" result-handler="resultHandler"></actions>
|
||||
<table
|
||||
hz-table ng-cloak
|
||||
<div hz-table
|
||||
track-rows-by="{$ config.trackId $}"
|
||||
ng-cloak
|
||||
st-magic-search
|
||||
st-table="items"
|
||||
st-safe-src="safeSrcItems"
|
||||
class="table table-striped table-rsp table-detail">
|
||||
class="container-fluid">
|
||||
|
||||
<div class="row hz-dynamic-table-preamble">
|
||||
<hz-magic-search-bar ng-if="filterFacets" class="col-md-{$ config.searchColumnSpan $}">
|
||||
</hz-magic-search-bar>
|
||||
<actions ng-if="batchActions" class="col-md-{$ config.actionColumnSpan $}"
|
||||
allowed="batchActions" type="batch" result-handler="resultHandler">
|
||||
</actions>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-rsp table-detail">
|
||||
<thead>
|
||||
<!--
|
||||
Table-column-headers:
|
||||
@ -29,6 +37,7 @@
|
||||
translate>
|
||||
{$ column.title $}
|
||||
</th>
|
||||
<th ng-if="itemActions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -55,12 +64,12 @@
|
||||
class="rsp-p{$ column.priority $}">
|
||||
<hz-cell></hz-cell>
|
||||
</td>
|
||||
<td class="action_column">
|
||||
<td ng-if="itemActions" class="actions_column">
|
||||
<!--
|
||||
Table-row-action-column:
|
||||
Actions taken here apply to a single item/row.
|
||||
-->
|
||||
<actions ng-if="itemActions" allowed="itemActions" type="row" item="item" result-handler="resultHandler"></actions>
|
||||
<actions allowed="itemActions" type="row" item="item" result-handler="resultHandler"></actions>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -85,4 +94,5 @@
|
||||
-->
|
||||
<tfoot hz-table-footer items="items"></tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</hz-magic-search-context>
|
||||
|
@ -25,7 +25,7 @@
|
||||
}
|
||||
|
||||
describe('hzDynamicTable directive', function () {
|
||||
var $scope, $compile, markup;
|
||||
var $scope, $compile, $qExtensions, markup;
|
||||
|
||||
beforeEach(module('templates'));
|
||||
beforeEach(module('smart-table'));
|
||||
@ -34,6 +34,7 @@
|
||||
beforeEach(inject(function ($injector) {
|
||||
$compile = $injector.get('$compile');
|
||||
$scope = $injector.get('$rootScope').$new();
|
||||
$qExtensions = $injector.get('horizon.framework.util.q.extensions');
|
||||
|
||||
$scope.config = {
|
||||
selectAll: true,
|
||||
@ -107,7 +108,7 @@
|
||||
it('has the correct responsive priority classes', function() {
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
expect($element.find('tbody tr').length).toBe(7);
|
||||
expect($element.find('tbody tr:eq(0) td').length).toBe(6);
|
||||
expect($element.find('tbody tr:eq(0) td').length).toBe(5);
|
||||
expect($element.find('tbody tr:eq(2) td:eq(2)').hasClass('rsp-p1')).toBe(true);
|
||||
expect($element.find('tbody tr:eq(2) td:eq(3)').hasClass('rsp-p2')).toBe(true);
|
||||
expect($element.find('tbody tr:eq(2) td:eq(4)').hasClass('rsp-p1')).toBe(true);
|
||||
@ -116,12 +117,63 @@
|
||||
it('has the correct number of rows (including detail rows and no items row)', function() {
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
expect($element.find('tbody tr').length).toBe(7);
|
||||
expect($element.find('tbody tr:eq(0) td').length).toBe(6);
|
||||
expect($element.find('tbody tr:eq(0) td').length).toBe(5);
|
||||
expect($element.find('tbody tr:eq(2) td:eq(2)').text()).toContain('snake');
|
||||
expect($element.find('tbody tr:eq(2) td:eq(3)').text()).toContain('reptile');
|
||||
expect($element.find('tbody tr:eq(2) td:eq(4)').text()).toContain('mice');
|
||||
});
|
||||
|
||||
it('has no search or action buttons if none configured', function() {
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
expect($element.find('.hz-dynamic-table-preamble').length).toBe(1);
|
||||
expect($element.find('.hz-dynamic-table-preamble').text().trim()).toBe('');
|
||||
});
|
||||
|
||||
describe('search & action button preamble', function () {
|
||||
beforeEach(function() {
|
||||
$scope.filterFacets = [{ label: 'Name', name: 'name' }];
|
||||
$scope.batchActions = [
|
||||
{
|
||||
id: 'action',
|
||||
service: {allowed: function () {
|
||||
return $qExtensions.booleanAsPromise(false);
|
||||
}},
|
||||
template: { type: 'create' }}
|
||||
];
|
||||
markup =
|
||||
'<hz-dynamic-table config="config" items="safeTableData" ' +
|
||||
'filter-facets="filterFacets" batch-actions="batchActions">' +
|
||||
'</hz-dynamic-table>';
|
||||
});
|
||||
|
||||
it('has the correct number of default columns', function() {
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
var preamble = $element.find('.hz-dynamic-table-preamble');
|
||||
expect(preamble.length).toBe(1);
|
||||
expect(preamble.find('hz-magic-search-bar').hasClass('col-md-12')).toBe(true);
|
||||
expect(preamble.find('actions').hasClass('col-md-12')).toBe(true);
|
||||
});
|
||||
|
||||
it('has the configured number of columns calculated', function() {
|
||||
$scope.config.searchColumnSpan = 7;
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
var preamble = $element.find('.hz-dynamic-table-preamble');
|
||||
expect(preamble.length).toBe(1);
|
||||
expect(preamble.find('hz-magic-search-bar').hasClass('col-md-7')).toBe(true);
|
||||
expect(preamble.find('actions').hasClass('col-md-5')).toBe(true);
|
||||
});
|
||||
|
||||
it('has the configured number of columns', function() {
|
||||
$scope.config.searchColumnSpan = 8;
|
||||
$scope.config.actionColumnSpan = 4;
|
||||
var $element = digestMarkup($scope, $compile, markup);
|
||||
var preamble = $element.find('.hz-dynamic-table-preamble');
|
||||
expect(preamble.length).toBe(1);
|
||||
expect(preamble.find('hz-magic-search-bar').hasClass('col-md-8')).toBe(true);
|
||||
expect(preamble.find('actions').hasClass('col-md-4')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hzDetailRow directive', function() {
|
||||
|
||||
it('compiles default detail row template', function() {
|
||||
|
@ -32,13 +32,18 @@
|
||||
* row collection and `st-safe-src` attribute to pass in the
|
||||
* safe row collection.
|
||||
*
|
||||
* If rows are identified by some property other than "id" (for the
|
||||
* purposes of selection) then use the track-rows-by attribute to
|
||||
* identify the property that should be used. In the example below,
|
||||
* the unique property for the rows is "name", not "id" (the default).
|
||||
*
|
||||
* @restrict A
|
||||
* @scope true
|
||||
* @example
|
||||
*
|
||||
* ```
|
||||
* <table st-table='displayedCollection' st-safe-src='rowCollection'
|
||||
* hz-table default-sort="email">
|
||||
* hz-table track-rows-by="name">
|
||||
* <thead>
|
||||
* <tr>
|
||||
* <th>
|
||||
@ -61,13 +66,21 @@
|
||||
*
|
||||
*/
|
||||
function hzTable() {
|
||||
var directive = {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'stTable',
|
||||
scope: true,
|
||||
controller: 'TableController',
|
||||
controllerAs: 'tCtrl'
|
||||
controllerAs: 'tCtrl',
|
||||
link: link
|
||||
};
|
||||
return directive;
|
||||
|
||||
///////////////////
|
||||
|
||||
function link(scope, element, attrs) {
|
||||
if (attrs.trackRowsBy) {
|
||||
scope.tCtrl.trackId = attrs.trackRowsBy;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
@ -38,6 +38,7 @@
|
||||
function TableController($scope) {
|
||||
|
||||
var ctrl = this;
|
||||
ctrl.trackId = 'id';
|
||||
ctrl.isSelected = isSelected;
|
||||
ctrl.toggleSelect = toggleSelect;
|
||||
ctrl.broadcastExpansion = broadcastExpansion;
|
||||
@ -57,7 +58,7 @@
|
||||
* return true if the row is selected
|
||||
*/
|
||||
function isSelected(row) {
|
||||
var rowState = ctrl.selections[row.id];
|
||||
var rowState = ctrl.selections[row[ctrl.trackId]];
|
||||
return angular.isDefined(rowState) && rowState.checked;
|
||||
}
|
||||
|
||||
@ -76,7 +77,12 @@
|
||||
* Toggle the row selection state
|
||||
*/
|
||||
function toggleSelect(row, checkedState, broadcast) {
|
||||
ctrl.selections[row.id] = { checked: checkedState, item: row };
|
||||
var key = row[ctrl.trackId];
|
||||
if (angular.isDefined(ctrl.selections[key])) {
|
||||
ctrl.selections[key].checked = checkedState;
|
||||
} else {
|
||||
ctrl.selections[key] = { checked: checkedState, item: row };
|
||||
}
|
||||
ctrl.selected = getSelected(ctrl.selections);
|
||||
if (broadcast) {
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user