Redesign failed tests panel
This redesigns the failed tests panel, replacing the panel/accordion group/table combination with a flat table that should be much easier to extend. This also adds a new 'nest' directive to better support dynamic hiding/showing of content. The directive is able to insert nested content as a sibling of any normal data row, allowing for more consistent layouts. Change-Id: Ic0ad498049ca0f008fbcecb1dbbac3c48ab9cb41
This commit is contained in:
parent
75f5954a9e
commit
a10d099d23
|
@ -150,7 +150,7 @@ function HomeController(
|
|||
if (vm.recentRuns[link].bugs.length === 0) {
|
||||
vm.recentRuns[link].bugs = '';
|
||||
} else {
|
||||
vm.recentRuns[link].bugs = 'Likely bugs: ' + vm.recentRuns[link].bugs.join();
|
||||
vm.recentRuns[link].bugs = vm.recentRuns[link].bugs.join();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
'use strict';
|
||||
|
||||
var directivesModule = require('./_index.js');
|
||||
|
||||
function maxColspan(element) {
|
||||
var thead = element.find('thead');
|
||||
|
||||
var max = 0;
|
||||
angular.forEach(element.find('tr'), function(row) {
|
||||
var count = angular.element(row).find('th').length;
|
||||
if (count > max) {
|
||||
max = count;
|
||||
}
|
||||
});
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
function nest($compile) {
|
||||
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
function controller($scope) {
|
||||
var self = this;
|
||||
self.open = false;
|
||||
|
||||
self.toggle = function() {
|
||||
self.open = !self.open;
|
||||
};
|
||||
|
||||
$scope.$watch('isOpen', function(value, old) {
|
||||
if (value === old) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.open = value;
|
||||
});
|
||||
}
|
||||
|
||||
function link(scope, el, attrs, ctrl) {
|
||||
var container = null;
|
||||
var dest = null;
|
||||
var tag = el.prop('tagName');
|
||||
if (tag === 'TR' || tag === 'TH') {
|
||||
// assumes table -> t(head|body) -> tr -> t(d|h)
|
||||
var table = el.parent().parent();
|
||||
var cols = maxColspan(table);
|
||||
|
||||
container = angular.element('<tr>');
|
||||
dest = angular.element('<td>');
|
||||
dest.attr('colspan', cols);
|
||||
container.append(dest);
|
||||
} else {
|
||||
dest = angular.element('<div>');
|
||||
container = dest;
|
||||
}
|
||||
|
||||
container.addClass('nest');
|
||||
|
||||
if (scope.nest) {
|
||||
// do some dirty hacks to make sure we pass the correct scope
|
||||
//
|
||||
// since we're appending as a sibling inside an ng-repeat, the effective
|
||||
// scope when the template renders will be wrong even though we compiled
|
||||
// and linked the element with our current scope
|
||||
// as a workaround, we'll make our scope available to the template as
|
||||
// 'scope' by literally passing '$parent' to the directive
|
||||
var include = angular.element('<nest-transclude ' +
|
||||
'template-url="' + scope.nest + '" ' +
|
||||
'scope="$parent"></nest-transclude>');
|
||||
$compile(include)(scope);
|
||||
dest.append(include);
|
||||
}
|
||||
|
||||
scope.$watch(function() { return ctrl.open; }, function(val, old) {
|
||||
if (val) {
|
||||
el.after(container);
|
||||
} else {
|
||||
container.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
'isOpen': '=',
|
||||
'nest': '@'
|
||||
},
|
||||
require: 'nest',
|
||||
controller: controller,
|
||||
controllerAs: 'nest',
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
directivesModule.directive('nest', nest);
|
||||
|
||||
function nestToggle() {
|
||||
function link(scope, el, attrs, nestController) {
|
||||
el.on('click', function() {
|
||||
nestController.toggle();
|
||||
scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '^^nest',
|
||||
scope: true,
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
directivesModule.directive('nestToggle', nestToggle);
|
||||
|
||||
function nestIndicator() {
|
||||
function link(scope, el, attrs, nestController) {
|
||||
var fa = angular.element('<i></i>');
|
||||
fa.addClass('fa fa-fw');
|
||||
el.append(fa);
|
||||
|
||||
function update() {
|
||||
if (fa.hasClass('fa-minus-square-o')) {
|
||||
fa.removeClass('fa-minus-square-o');
|
||||
}
|
||||
|
||||
if (fa.hasClass('fa-plus-square-o')) {
|
||||
fa.removeClass('fa-plus-square-o');
|
||||
}
|
||||
|
||||
if (nestController.open) {
|
||||
fa.addClass('fa-minus-square-o');
|
||||
} else {
|
||||
fa.addClass('fa-plus-square-o');
|
||||
}
|
||||
}
|
||||
|
||||
scope.$watch(function() {
|
||||
return nestController.open;
|
||||
}, function(val, old) {
|
||||
if (val === old) {
|
||||
return;
|
||||
}
|
||||
|
||||
update();
|
||||
});
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '^^nest',
|
||||
scope: true,
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
directivesModule.directive('nestIndicator', nestIndicator);
|
||||
|
||||
function nestTransclude() {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
transclude: true,
|
||||
scope: {
|
||||
'scope': '='
|
||||
},
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl;
|
||||
}
|
||||
};
|
||||
}
|
||||
directivesModule.directive('nestTransclude', nestTransclude);
|
|
@ -32,7 +32,11 @@ table.status-table {
|
|||
|
||||
table.default-cols {
|
||||
thead {
|
||||
th {
|
||||
.tiny {
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
th:not(.tiny) {
|
||||
white-space: nowrap;
|
||||
min-width: 75px;
|
||||
}
|
||||
|
@ -122,6 +126,10 @@ h1.page-header {
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
h1.page-header {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.feedback {
|
||||
border: 1px solid #ccc;
|
||||
background-color: #f5f5f5;
|
||||
|
@ -136,3 +144,18 @@ h1.page-header {
|
|||
padding: 1em;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
[nest-toggle] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tr.nest {
|
||||
nest-transclude table {
|
||||
background-color: transparent !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
> td {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,40 +61,42 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel panel-default table-responsive">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Failed Tests in Last 10 Failed Runs</h3>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<uib-accordion close-others="false">
|
||||
<div ng-repeat="(key, value) in home.recentRuns">
|
||||
<uib-accordion-group
|
||||
template-url="templates/accordion-group-run.html"
|
||||
heading="{{ key }};{{ value.bugs }}"
|
||||
is-open="false">
|
||||
<table table-sort data="value.fails"
|
||||
class="table table-hover default-cols">
|
||||
<thead>
|
||||
<tr>
|
||||
<th sort-field="test_id">Test ID</th>
|
||||
<th sort-field="start_time">Start Time</th>
|
||||
<th sort-default sort-field="stop_time">Stop Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="(k, v) in value.fails">
|
||||
<td><a ui-sref="test({ testId: v.test_id })"}>
|
||||
{{ v.test_id }}</a>
|
||||
</td>
|
||||
<td>{{ v.start_time | date:'M/d/yyyy HH:mm' }}</td>
|
||||
<td>{{ v.stop_time | date:'M/d/yyyy HH:mm' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</uib-accordion-group>
|
||||
</div>
|
||||
</uib-accordion>
|
||||
</div>
|
||||
<table class="table table-hover default-cols">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tiny"></th>
|
||||
<th>Job</th>
|
||||
<th>Artifacts Link</th>
|
||||
<th># Failed</th>
|
||||
<th>Likely Bugs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr nest="templates/run-details.html"
|
||||
ng-repeat="(key, value) in home.recentRuns">
|
||||
|
||||
<td><span nest-toggle nest-indicator></span></td>
|
||||
<td><a href nest-toggle>{{key | split:'/' | last:2 | pick:0}}</a></td>
|
||||
<td>
|
||||
<a target="_blank" href="{{ key }}">
|
||||
{{key | split:'/' | last:2 | pick:1}}
|
||||
<fa name="external-link"></fa>
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ value.fails.length }}</td>
|
||||
<td>
|
||||
<span ng-if="!!value.bugs">
|
||||
{{ scope.value.bugs}}
|
||||
</span>
|
||||
<span ng-if="!value.bugs">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<div class="panel" ng-class="panelClass || 'panel-default'">
|
||||
<div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="panel-heading" ng-keypress="toggleOpen($event)">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}" tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading">
|
||||
<span uib-accordion-header ng-class="{'text-muted': isDisabled}">
|
||||
<i ng-class="{'fa fa-minus-square-o': isOpen, 'fa fa-plus-square-o': !isOpen}"></i>
|
||||
{{heading | split:';' | first | join:'' | split:'/' | last:2 | join:'/'}}
|
||||
</span>
|
||||
</a>
|
||||
<span class="text-info"><a target="_blank" href="{{heading | split:';' | first | join:''}}"><fa name="external-link"></fa></a></span>
|
||||
<span class="pull-right">
|
||||
{{heading | split:';' | last | join:''}}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel" class="panel-collapse collapse" uib-collapse="!isOpen">
|
||||
<div class="panel-body" ng-transclude></div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,20 @@
|
|||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th sort-field="test_id">Test ID</th>
|
||||
<th sort-field="start_time">Start Time</th>
|
||||
<th sort-default sort-field="stop_time">Stop Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="(k, v) in scope.value.fails">
|
||||
<td>
|
||||
<a ui-sref="test({ testId: v.test_id })"}>
|
||||
{{ v.test_id }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ v.start_time | date:'M/d/yyyy HH:mm' }}</td>
|
||||
<td>{{ v.stop_time | date:'M/d/yyyy HH:mm' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
Loading…
Reference in New Issue