Merge "Indicate table loading, error, and empty states"
This commit is contained in:
commit
7597e0a10e
|
@ -67,3 +67,15 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Progress indicator in the table while items are loading */
|
||||
[table-status] {
|
||||
.progress {
|
||||
margin: 0px auto;
|
||||
width: 25%;
|
||||
height: $line-height-computed;
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,6 +46,8 @@
|
|||
var ctrl = this;
|
||||
ctrl.items = [];
|
||||
ctrl.src = [];
|
||||
ctrl.loading = true;
|
||||
ctrl.error = false;
|
||||
ctrl.checked = {};
|
||||
ctrl.loadbalancerId = $routeParams.loadbalancerId;
|
||||
ctrl.batchActions = batchActions.init(ctrl.loadbalancerId);
|
||||
|
@ -56,11 +58,21 @@
|
|||
////////////////////////////////
|
||||
|
||||
function init() {
|
||||
api.getListeners(ctrl.loadbalancerId).success(success);
|
||||
ctrl.src = [];
|
||||
ctrl.loading = true;
|
||||
ctrl.error = false;
|
||||
api.getListeners(ctrl.loadbalancerId).then(success, fail);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
ctrl.src = response.items;
|
||||
ctrl.src = response.data.items;
|
||||
ctrl.loading = false;
|
||||
}
|
||||
|
||||
function fail(/*response*/) {
|
||||
ctrl.src = [];
|
||||
ctrl.loading = false;
|
||||
ctrl.error = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,12 +18,17 @@
|
|||
|
||||
describe('LBaaS v2 Listeners Table Controller', function() {
|
||||
var controller, lbaasv2API, rowActions, batchActions;
|
||||
var items = [];
|
||||
var items = [{ foo: 'bar' }];
|
||||
var apiFail = false;
|
||||
|
||||
function fakeAPI() {
|
||||
return {
|
||||
success: function(callback) {
|
||||
callback({ items: items });
|
||||
then: function(success, fail) {
|
||||
if (apiFail && fail) {
|
||||
fail();
|
||||
} else {
|
||||
success({ data: { items: items } });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -64,6 +69,8 @@
|
|||
var ctrl = createController();
|
||||
expect(ctrl.items).toEqual([]);
|
||||
expect(ctrl.src).toEqual(items);
|
||||
expect(ctrl.loading).toBe(false);
|
||||
expect(ctrl.error).toBe(false);
|
||||
expect(ctrl.checked).toEqual({});
|
||||
expect(ctrl.loadbalancerId).toEqual('1234');
|
||||
expect(rowActions.init).toHaveBeenCalledWith(ctrl.loadbalancerId);
|
||||
|
@ -74,13 +81,16 @@
|
|||
});
|
||||
|
||||
it('should invoke lbaasv2 apis', function() {
|
||||
createController();
|
||||
var ctrl = createController();
|
||||
expect(lbaasv2API.getListeners).toHaveBeenCalled();
|
||||
expect(ctrl.src.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should init the rowactions', function() {
|
||||
createController();
|
||||
expect(lbaasv2API.getListeners).toHaveBeenCalled();
|
||||
it('should show error if loading fails', function() {
|
||||
apiFail = true;
|
||||
var ctrl = createController();
|
||||
expect(ctrl.src.length).toBe(0);
|
||||
expect(ctrl.error).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -112,6 +112,8 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr table-status table="table" column-count="7"></tr>
|
||||
|
||||
</tbody>
|
||||
<!--
|
||||
Table-footer:
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
var ctrl = this;
|
||||
ctrl.items = [];
|
||||
ctrl.src = [];
|
||||
ctrl.loading = true;
|
||||
ctrl.error = false;
|
||||
ctrl.checked = {};
|
||||
ctrl.batchActions = batchActions;
|
||||
ctrl.rowActions = rowActions;
|
||||
|
@ -57,11 +59,20 @@
|
|||
////////////////////////////////
|
||||
|
||||
function init() {
|
||||
api.getLoadBalancers(true).success(success);
|
||||
ctrl.src = [];
|
||||
ctrl.loading = true;
|
||||
api.getLoadBalancers(true).then(success, fail);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
ctrl.src = response.items;
|
||||
ctrl.src = response.data.items;
|
||||
ctrl.loading = false;
|
||||
}
|
||||
|
||||
function fail(/*response*/) {
|
||||
ctrl.src = [];
|
||||
ctrl.error = true;
|
||||
ctrl.loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,12 +18,17 @@
|
|||
|
||||
describe('LBaaS v2 Load Balancers Table Controller', function() {
|
||||
var controller, lbaasv2API, scope;
|
||||
var items = [];
|
||||
var items = [{ foo: 'bar' }];
|
||||
var apiFail = false;
|
||||
|
||||
function fakeAPI() {
|
||||
return {
|
||||
success: function(callback) {
|
||||
callback({ items: items });
|
||||
then: function(success, fail) {
|
||||
if (apiFail && fail) {
|
||||
fail();
|
||||
} else {
|
||||
success({ data: { items: items } });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -55,6 +60,8 @@
|
|||
var ctrl = createController();
|
||||
expect(ctrl.items).toEqual([]);
|
||||
expect(ctrl.src).toEqual(items);
|
||||
expect(ctrl.loading).toBe(false);
|
||||
expect(ctrl.error).toBe(false);
|
||||
expect(ctrl.checked).toEqual({});
|
||||
expect(ctrl.batchActions).toBeDefined();
|
||||
expect(ctrl.rowActions).toBeDefined();
|
||||
|
@ -63,8 +70,16 @@
|
|||
});
|
||||
|
||||
it('should invoke lbaasv2 apis', function() {
|
||||
createController();
|
||||
var ctrl = createController();
|
||||
expect(lbaasv2API.getLoadBalancers).toHaveBeenCalled();
|
||||
expect(ctrl.src.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should show error if loading fails', function() {
|
||||
apiFail = true;
|
||||
var ctrl = createController();
|
||||
expect(ctrl.src.length).toBe(0);
|
||||
expect(ctrl.error).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -143,6 +143,8 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr table-status table="table" column-count="9"></tr>
|
||||
|
||||
</tbody>
|
||||
<!--
|
||||
Table-footer:
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
var ctrl = this;
|
||||
ctrl.items = [];
|
||||
ctrl.src = [];
|
||||
ctrl.loading = true;
|
||||
ctrl.error = false;
|
||||
ctrl.checked = {};
|
||||
ctrl.loadbalancerId = $routeParams.loadbalancerId;
|
||||
ctrl.listenerId = $routeParams.listenerId;
|
||||
|
@ -58,11 +60,21 @@
|
|||
////////////////////////////////
|
||||
|
||||
function init() {
|
||||
api.getMembers(ctrl.poolId).success(success);
|
||||
ctrl.src = [];
|
||||
ctrl.loading = true;
|
||||
ctrl.error = false;
|
||||
api.getMembers(ctrl.poolId).then(success, fail);
|
||||
}
|
||||
|
||||
function success(response) {
|
||||
ctrl.src = response.items;
|
||||
ctrl.src = response.data.items;
|
||||
ctrl.loading = false;
|
||||
}
|
||||
|
||||
function fail(/*response*/) {
|
||||
ctrl.src = [];
|
||||
ctrl.loading = false;
|
||||
ctrl.error = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,12 +18,17 @@
|
|||
|
||||
describe('LBaaS v2 Members Table Controller', function() {
|
||||
var controller, lbaasv2API, scope;
|
||||
var items = [];
|
||||
var items = [{ foo: 'bar' }];
|
||||
var apiFail = false;
|
||||
|
||||
function fakeAPI() {
|
||||
return {
|
||||
success: function(callback) {
|
||||
callback({ items: items });
|
||||
then: function(success, fail) {
|
||||
if (apiFail && fail) {
|
||||
fail();
|
||||
} else {
|
||||
success({ data: { items: items } });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -60,6 +65,8 @@
|
|||
var ctrl = createController();
|
||||
expect(ctrl.items).toEqual([]);
|
||||
expect(ctrl.src).toEqual(items);
|
||||
expect(ctrl.loading).toBe(false);
|
||||
expect(ctrl.error).toBe(false);
|
||||
expect(ctrl.checked).toEqual({});
|
||||
expect(ctrl.loadbalancerId).toBeDefined();
|
||||
expect(ctrl.listenerId).toBeDefined();
|
||||
|
@ -68,8 +75,16 @@
|
|||
});
|
||||
|
||||
it('should invoke lbaasv2 apis', function() {
|
||||
createController();
|
||||
var ctrl = createController();
|
||||
expect(lbaasv2API.getMembers).toHaveBeenCalled();
|
||||
expect(ctrl.src.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should show error if loading fails', function() {
|
||||
apiFail = true;
|
||||
var ctrl = createController();
|
||||
expect(ctrl.src.length).toBe(0);
|
||||
expect(ctrl.error).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -73,6 +73,8 @@
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr table-status table="table" column-count="6"></tr>
|
||||
|
||||
</tbody>
|
||||
<!--
|
||||
Table-footer:
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2016 IBM 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.dashboard.project.lbaasv2')
|
||||
.directive('tableStatus', tableStatus);
|
||||
|
||||
tableStatus.$inject = [
|
||||
'horizon.dashboard.project.lbaasv2.basePath'
|
||||
];
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name horizon.dashboard.project.lbaasv2:tableStatus
|
||||
* @description
|
||||
* The `tableStatus` directive provides a status indicator while loading a table. The table
|
||||
* should have loading and error properties that give the status of the table, and an items
|
||||
* array that holds the items being displayed in the table. The column count can be provided
|
||||
* to fit the status row to an exact number of columns.
|
||||
* @restrict A
|
||||
*
|
||||
* @example
|
||||
* ```
|
||||
* <tr table-status table="table" column-count="9"></tr>
|
||||
* ```
|
||||
*/
|
||||
|
||||
function tableStatus(basePath) {
|
||||
var directive = {
|
||||
restrict: 'A',
|
||||
templateUrl: basePath + 'widgets/table/table-status.html',
|
||||
scope: {
|
||||
table: '=',
|
||||
columnCount: '=?'
|
||||
}
|
||||
};
|
||||
return directive;
|
||||
}
|
||||
}());
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2016 IBM 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';
|
||||
|
||||
function digestMarkup(scope, compile, markup) {
|
||||
var element = angular.element(markup);
|
||||
compile(element)(scope);
|
||||
scope.$apply();
|
||||
return element;
|
||||
}
|
||||
|
||||
describe('tableStatus directive', function() {
|
||||
var $scope, $compile, markup, table;
|
||||
|
||||
beforeEach(module('templates'));
|
||||
beforeEach(module('horizon.dashboard.project.lbaasv2'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
$compile = $injector.get('$compile');
|
||||
$scope = $injector.get('$rootScope').$new();
|
||||
table = {
|
||||
loading: true,
|
||||
error: false,
|
||||
items: []
|
||||
};
|
||||
$scope.table = table;
|
||||
markup = '<tr table-status table="table"></tr>';
|
||||
}));
|
||||
|
||||
it('initially shows loading status', function() {
|
||||
var element = digestMarkup($scope, $compile, markup);
|
||||
expect(element).toBeDefined();
|
||||
|
||||
expect(element.children().length).toBe(1);
|
||||
expect(element.children().first().hasClass('no-rows-help')).toBe(false);
|
||||
expect(element.find('.progress-bar').hasClass('progress-bar-striped')).toBe(true);
|
||||
expect(element.find('.progress-bar').hasClass('progress-bar-danger')).toBe(false);
|
||||
expect(element.find('.progress-bar > span').length).toBe(1);
|
||||
expect(element.find('.progress-bar > span').hasClass('sr-only')).toBe(true);
|
||||
});
|
||||
|
||||
it('indicates error status on error', function() {
|
||||
var element = digestMarkup($scope, $compile, markup);
|
||||
expect(element).toBeDefined();
|
||||
|
||||
table.loading = false;
|
||||
table.error = true;
|
||||
$scope.$apply();
|
||||
|
||||
expect(element.children().length).toBe(1);
|
||||
expect(element.children().first().hasClass('no-rows-help')).toBe(false);
|
||||
expect(element.find('.progress-bar').hasClass('progress-bar-striped')).toBe(false);
|
||||
expect(element.find('.progress-bar').hasClass('progress-bar-danger')).toBe(true);
|
||||
expect(element.find('.progress-bar > span').length).toBe(1);
|
||||
expect(element.find('.progress-bar > span').hasClass('sr-only')).toBe(false);
|
||||
expect(element.find('.progress-bar > span').text().trim())
|
||||
.toBe('An error occurred. Please try again later.');
|
||||
});
|
||||
|
||||
it('indicates no rows when there are no rows to display', function() {
|
||||
var element = digestMarkup($scope, $compile, markup);
|
||||
expect(element).toBeDefined();
|
||||
|
||||
table.loading = false;
|
||||
table.error = false;
|
||||
$scope.$apply();
|
||||
|
||||
expect(element.children().length).toBe(1);
|
||||
expect(element.children().first().hasClass('no-rows-help')).toBe(true);
|
||||
expect(element.find('.progress').length).toBe(0);
|
||||
expect(element.find('span').length).toBe(1);
|
||||
expect(element.find('span').text().trim()).toBe('No items to display.');
|
||||
});
|
||||
|
||||
it('goes away when done loading and there are rows to display', function() {
|
||||
var element = digestMarkup($scope, $compile, markup);
|
||||
expect(element).toBeDefined();
|
||||
|
||||
table.loading = false;
|
||||
table.error = false;
|
||||
table.items = ['foo'];
|
||||
$scope.$apply();
|
||||
|
||||
expect(element.children().length).toBe(0);
|
||||
});
|
||||
|
||||
});
|
||||
}());
|
|
@ -0,0 +1,12 @@
|
|||
<td ng-if="table.loading || table.error || table.items.length === 0"
|
||||
colspan="{$ columnCount || 100 $}"
|
||||
ng-class="{ 'no-rows-help': !table.loading && !table.error }">
|
||||
<span translate ng-if="!table.loading && !table.error">No items to display.</span>
|
||||
<div class="progress" ng-if="table.loading || table.error">
|
||||
<div class="progress-bar" role="progressbar"
|
||||
ng-class="{ 'progress-bar-striped active': !table.error, 'progress-bar-danger': table.error }">
|
||||
<span ng-if="!table.error" class="sr-only" translate>Loading</span>
|
||||
<span ng-if="table.error" translate>An error occurred. Please try again later.</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
Loading…
Reference in New Issue