зеркало из https://github.com/mozilla/treeherder.git
Bug 1163064 - Collapse test group chunks down to counts by result and state
This branch aggregates non-failed jobs within groups and shows the counts of each result/state within.
A ``+`` precedes each count to indicate that's what it is.
A few points of interest:
* Clicking any group or count will expand or collapse the group. This choice is
remembered as jobs are updated and filters are changed. Page reload resets it.
* Clicking on the ``( + )`` and ``( - )`` buttons will toggle groups expanded/collapsed
globally. This is persisted in the URL. Clicking this button will reset any group
expand/collapse state that was set individually.
* Failed Unclassified jobs are never put into "counts". They show as top-level
jobs and are still hit with the ``n`` and ``p`` hot keys, etc.
Also fixes:
* Bug 1191454 - classifying 3 jobs, one selected, then clicking "n" for next will go to the first
* Bug 1191487 - Re-scroll selected job into view during chunk expand/collapse
This reverts commit 2e6b7b56c6
.
This commit is contained in:
Родитель
c0c553e62d
Коммит
e633a67640
|
@ -594,6 +594,11 @@ th-watched-repo {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.group-state-nav-icon {
|
||||
width: 7px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.job-group {
|
||||
margin: 0 -3px 0 3px;
|
||||
}
|
||||
|
@ -606,11 +611,52 @@ th-watched-repo {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.group-btn {
|
||||
background: transparent;
|
||||
padding: 0 2px 0 2px;
|
||||
vertical-align: 0;
|
||||
line-height: 1.32;
|
||||
cursor: pointer;
|
||||
}
|
||||
.group-btn::before {
|
||||
content: "+";
|
||||
}
|
||||
|
||||
.group-symbol:hover {
|
||||
background-color: rgba(208, 228, 250, 0.51);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.group-content {
|
||||
margin-left: -3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.group-content::before {
|
||||
content: "("
|
||||
}
|
||||
|
||||
.group-content::after {
|
||||
content: ")"
|
||||
}
|
||||
|
||||
.group-count-list:hover {
|
||||
background-color: rgba(208, 228, 250, 0.51);
|
||||
}
|
||||
|
||||
.group-job-list {
|
||||
margin-left: -3px;
|
||||
}
|
||||
|
||||
.selected-job {
|
||||
border: 4px solid;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.selected-count.btn-lg-xform {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.filter-shown {
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -1255,7 +1301,14 @@ ul.failure-summary-list li .btn-xs {
|
|||
.job-btn.btn-ltgray,
|
||||
.job-btn.btn-green,
|
||||
.job-btn.btn-dkblue,
|
||||
.job-btn.btn-pink {
|
||||
.job-btn.btn-yellow,
|
||||
.job-btn.btn-pink,
|
||||
.group-btn.btn-dkgray-count,
|
||||
.group-btn.btn-ltgray-count,
|
||||
.group-btn.btn-green-count,
|
||||
.group-btn.btn-dkblue-count,
|
||||
.group-btn.btn-yellow-count,
|
||||
.group-btn.btn-pink-count {
|
||||
margin: 0 -5px 0 -1px;
|
||||
}
|
||||
|
||||
|
@ -1265,6 +1318,29 @@ ul.failure-summary-list li .btn-xs {
|
|||
margin: 0 -3px 1px 0;
|
||||
}
|
||||
|
||||
.btn-orange-classified::after,
|
||||
.btn-orange-classified-count::after,
|
||||
.btn-red-classified::after,
|
||||
.btn-red-classified-count::after,
|
||||
.btn-black-classified::after,
|
||||
.btn-black-classified-count::after,
|
||||
.btn-green-classified::after,
|
||||
.btn-green-classified-count::after,
|
||||
.btn-dkblue-classified::after,
|
||||
.btn-dkblue-classified-count::after,
|
||||
.btn-dkgray-classified::after,
|
||||
.btn-dkgray-classified-count::after,
|
||||
.btn-ltgray-classified::after,
|
||||
.btn-ltgray-classified-count::after,
|
||||
.btn-yellow-classified::after,
|
||||
.btn-yellow-classified-count::after,
|
||||
.btn-pink-classified::after,
|
||||
.btn-pink-classified-count::after,
|
||||
.btn-purple-classified::after,
|
||||
.btn-purple-classified-count::after {
|
||||
content: "*";
|
||||
}
|
||||
|
||||
.btn-view-nav {
|
||||
background-color: transparent;
|
||||
border-color: #373d40;
|
||||
|
@ -1402,7 +1478,8 @@ fieldset[disabled] .btn-orange.active {
|
|||
border-color: #dd6602;
|
||||
}
|
||||
|
||||
.btn-orange-classified {
|
||||
.btn-orange-classified,
|
||||
.btn-orange-classified-count {
|
||||
color: #dd6602;
|
||||
}
|
||||
.btn-orange-classified:hover,
|
||||
|
@ -1459,7 +1536,8 @@ fieldset[disabled] .btn-red.active {
|
|||
border-color: #c2020e;
|
||||
}
|
||||
|
||||
.btn-red-classified {
|
||||
.btn-red-classified,
|
||||
.btn-red-classified-count {
|
||||
color: #90010a;
|
||||
}
|
||||
.btn-red-classified:hover,
|
||||
|
@ -1487,7 +1565,10 @@ fieldset[disabled] .btn-red-classified.active {
|
|||
color: white;
|
||||
}
|
||||
|
||||
.btn-dkblue {
|
||||
.btn-dkblue,
|
||||
.btn-dkblue-count,
|
||||
.btn-dkblue-classified,
|
||||
.btn-dkblue-classified-count {
|
||||
color: #283aa2;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -1516,7 +1597,10 @@ fieldset[disabled] .btn-dkblue.active {
|
|||
border-color: #2d48d6;
|
||||
}
|
||||
|
||||
.btn-green {
|
||||
.btn-green,
|
||||
.btn-green-count,
|
||||
.btn-green-classified,
|
||||
.btn-green-classified-count {
|
||||
color: rgba(2, 130, 51, 0.75);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -1543,7 +1627,8 @@ fieldset[disabled] .btn-green.active {
|
|||
border-color: #02c238;
|
||||
}
|
||||
|
||||
.btn-purple {
|
||||
.btn-purple
|
||||
.btn-purple-count {
|
||||
background-color: #9a7da6;
|
||||
border-color: #6f0296;
|
||||
color: white;
|
||||
|
@ -1573,7 +1658,8 @@ fieldset[disabled] .btn-purple.active {
|
|||
color: white;
|
||||
}
|
||||
|
||||
.btn-purple-classified {
|
||||
.btn-purple-classified,
|
||||
.btn-purple-classified-count {
|
||||
color: #6f0296;
|
||||
}
|
||||
.btn-purple-classified:hover,
|
||||
|
@ -1601,7 +1687,10 @@ fieldset[disabled] .btn-purple-classified.active {
|
|||
color: white;
|
||||
}
|
||||
|
||||
.btn-yellow {
|
||||
.btn-yellow,
|
||||
.btn-yellow-count,
|
||||
.btn-yellow-classified,
|
||||
.btn-yellow-classified-count {
|
||||
color: #cdce1d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -1627,7 +1716,10 @@ fieldset[disabled] .btn-yellow.active {
|
|||
border-color: #cdce1d;
|
||||
}
|
||||
|
||||
.btn-ltgray {
|
||||
.btn-ltgray,
|
||||
.btn-ltgray-count,
|
||||
.btn-ltgray-classified,
|
||||
.btn-ltgray-classified-count {
|
||||
color: #e0e0e0;
|
||||
}
|
||||
.btn-ltgray:hover,
|
||||
|
@ -1653,7 +1745,11 @@ fieldset[disabled] .btn-ltgray.active {
|
|||
border-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.btn-mdgray {
|
||||
.btn-mdgray,
|
||||
.btn-mdgray-count,
|
||||
.btn-mdgray-classified,
|
||||
.btn-mdgray-classified-count
|
||||
{
|
||||
background-color: #bfbfbf;
|
||||
border-color: #bfbfbf;
|
||||
}
|
||||
|
@ -1700,7 +1796,10 @@ fieldset[disabled] .btn-resultset:hover {
|
|||
color: white;
|
||||
}
|
||||
|
||||
.btn-dkgray {
|
||||
.btn-dkgray,
|
||||
.btn-dkgray-count,
|
||||
.btn-dkgray-classified,
|
||||
.btn-dkgray-classified-count {
|
||||
color: #7c7a7d;
|
||||
}
|
||||
.btn-dkgray:hover,
|
||||
|
@ -1728,7 +1827,10 @@ fieldset[disabled] .btn-dkgray.active {
|
|||
color: white;
|
||||
}
|
||||
|
||||
.btn-black {
|
||||
.btn-black
|
||||
.btn-black-count,
|
||||
.btn-black-classified,
|
||||
.btn-black-classified-count {
|
||||
background-color: #4a4a4a;
|
||||
border-color: #000000;
|
||||
color: white;
|
||||
|
@ -1781,7 +1883,10 @@ fieldset[disabled] .btn-black.active {
|
|||
border-color: #000000;
|
||||
}
|
||||
|
||||
.btn-pink {
|
||||
.btn-pink
|
||||
.btn-pink-count,
|
||||
.btn-pink-classified,
|
||||
.btn-pink-classified-count {
|
||||
color: #ff40d9;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
@ -171,13 +171,26 @@
|
|||
</script>
|
||||
|
||||
<!-- Start span for job groups -->
|
||||
<script type="'text/ng-template'" id="jobGroupBeginClone.html">
|
||||
<script type="'text/ng-template'" id="jobGroupClone.html">
|
||||
<span class="platform-group">
|
||||
<span class="disabled job-group" title="{{ name }}">{{ symbol }}(</span>
|
||||
<span class="job-group-list"></span>)
|
||||
<span class="disabled job-group" title="{{ name }}"
|
||||
data-grkey="{{ grkey }}">
|
||||
<span class="group-symbol"
|
||||
ignore-job-clear-on-click>{{ symbol }}</span>
|
||||
<span class="group-content">
|
||||
<span class="group-job-list"></span>
|
||||
<span class="group-count-list"></span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</script>
|
||||
|
||||
<!-- Job group count span for each count item -->
|
||||
<script type="'text/ng-template'" id="jobGroupCountClone.html">
|
||||
<button class="btn {{ selectedClasses }} group-btn btn-xs job-group-count {{ btnClass }}"
|
||||
title="{{ title }}">{{ value }}</button>
|
||||
</script>
|
||||
|
||||
<!-- Job Btn span -->
|
||||
<script type="'text/ng-template'" id="jobBtnClone.html">
|
||||
<button class="btn job-btn btn-xs {{ btnClass }} {{ key }}"
|
||||
|
|
|
@ -319,6 +319,17 @@ treeherderApp.controller('MainCtrl', [
|
|||
|
||||
};
|
||||
|
||||
$scope.getGroupState = function() {
|
||||
return $location.search().group_state || "collapsed";
|
||||
};
|
||||
|
||||
$scope.groupState = $scope.getGroupState();
|
||||
|
||||
$scope.toggleGroupState = function() {
|
||||
var newGroupState = $scope.groupState === "collapsed" ? "expanded" : null;
|
||||
$location.search("group_state", newGroupState);
|
||||
};
|
||||
|
||||
var getNewReloadTriggerParams = function() {
|
||||
return _.pick(
|
||||
$location.search(),
|
||||
|
@ -364,6 +375,13 @@ treeherderApp.controller('MainCtrl', [
|
|||
}
|
||||
$rootScope.skipNextPageReload = false;
|
||||
|
||||
// handle a change in the groupState whether it was by the button
|
||||
// or directly in the url.
|
||||
var newGroupState = $scope.getGroupState();
|
||||
if (newGroupState !== $scope.groupState) {
|
||||
$scope.groupState = newGroupState;
|
||||
$rootScope.$emit(thEvents.groupStateChanged);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.changeRepo = function(repo_name) {
|
||||
|
|
|
@ -23,6 +23,7 @@ treeherder.directive('thCloneJobs', [
|
|||
// CSS classes
|
||||
var btnCls = 'btn-xs';
|
||||
var selectedBtnCls = 'selected-job';
|
||||
var selectedCountCls = 'selected-count';
|
||||
var largeBtnCls = 'btn-lg-xform';
|
||||
|
||||
var col5Cls = 'col-xs-5';
|
||||
|
@ -31,8 +32,13 @@ treeherder.directive('thCloneJobs', [
|
|||
var jobListNoPadCls = 'job-list-nopad';
|
||||
var jobListPadCls = 'job-list-pad';
|
||||
|
||||
var viewContentSel = ".th-view-content";
|
||||
|
||||
var failResults = ["testfailed", "busted", "exception"];
|
||||
|
||||
// Custom Attributes
|
||||
var jobKeyAttr = 'data-jmkey';
|
||||
var groupKeyAttr = 'data-grkey';
|
||||
|
||||
var tableInterpolator = thCloneHtml.get('resultsetClone').interpolator;
|
||||
|
||||
|
@ -40,7 +46,10 @@ treeherder.directive('thCloneJobs', [
|
|||
var platformInterpolator = thCloneHtml.get('platformClone').interpolator;
|
||||
|
||||
//Instantiate job group interpolator
|
||||
var jobGroupInterpolator = thCloneHtml.get('jobGroupBeginClone').interpolator;
|
||||
var jobGroupInterpolator = thCloneHtml.get('jobGroupClone').interpolator;
|
||||
|
||||
//Instantiate job group count interpolator
|
||||
var jobGroupCountInterpolator = thCloneHtml.get('jobGroupCountClone').interpolator;
|
||||
|
||||
//Instantiate job btn interpolator
|
||||
var jobBtnInterpolator = thCloneHtml.get('jobBtnClone').interpolator;
|
||||
|
@ -85,9 +94,9 @@ treeherder.directive('thCloneJobs', [
|
|||
// The .selected-job can be invisible, for instance, when filtered to
|
||||
// unclassified failures only, and you then classify the selected job.
|
||||
// It's still selected, but no longer visible.
|
||||
jobs = $(".th-view-content").find(jobNavSelector.selector).filter(":visible, .selected-job");
|
||||
jobs = $(viewContentSel).find(jobNavSelector.selector).filter(":visible, .selected-job, .selected-count");
|
||||
if (jobs.length) {
|
||||
var selIdx = jobs.index(jobs.filter(".selected-job"));
|
||||
var selIdx = jobs.index(jobs.filter(".selected-job, .selected-count").first());
|
||||
var idx = getIndex(selIdx, jobs);
|
||||
|
||||
el = $(jobs[idx]);
|
||||
|
@ -126,20 +135,12 @@ treeherder.directive('thCloneJobs', [
|
|||
};
|
||||
|
||||
var setSelectJobStyles = function(el){
|
||||
|
||||
var lastJobSelected = ThResultSetStore.getSelectedJob(
|
||||
$rootScope.repoName);
|
||||
|
||||
if(!_.isEmpty(lastJobSelected.el)){
|
||||
lastJobSelected.el.removeClass(selectedBtnCls);
|
||||
lastJobSelected.el.removeClass(largeBtnCls);
|
||||
lastJobSelected.el.addClass(btnCls);
|
||||
}
|
||||
// clear the styles from the previously selected job, if any.
|
||||
clearSelectJobStyles();
|
||||
|
||||
el.removeClass(btnCls);
|
||||
el.addClass(largeBtnCls);
|
||||
el.addClass(selectedBtnCls);
|
||||
|
||||
};
|
||||
|
||||
var clearSelectJobStyles = function() {
|
||||
|
@ -151,6 +152,16 @@ treeherder.directive('thCloneJobs', [
|
|||
lastJobSelected.el.removeClass(largeBtnCls);
|
||||
lastJobSelected.el.addClass(btnCls);
|
||||
}
|
||||
|
||||
// if a job was previously selected that is now inside a count,
|
||||
// then the count will have the ``.selected-count`` class. Since
|
||||
// we are now selecting a job, we need to remove that class from the
|
||||
// count.
|
||||
var selectedCount = $(viewContentSel).find("."+selectedCountCls);
|
||||
if (selectedCount.length) {
|
||||
selectedCount.removeClass(selectedCountCls);
|
||||
selectedCount.removeClass(largeBtnCls);
|
||||
}
|
||||
};
|
||||
|
||||
var broadcastJobChangedTimeout = null;
|
||||
|
@ -166,87 +177,180 @@ treeherder.directive('thCloneJobs', [
|
|||
}, 200);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clicking a group will expand or collapse it. Expanded shows all
|
||||
* jobs. Collapsed shows counts and failed jobs.
|
||||
*/
|
||||
var clickGroupCb = function(el) {
|
||||
var groupMap = ThResultSetStore.getGroupMap($rootScope.repoName);
|
||||
var gi = getGroupInfo(el, groupMap);
|
||||
if (gi) {
|
||||
if (isGroupExpanded(gi.jgObj)) {
|
||||
gi.jgObj.groupState = "collapsed";
|
||||
addGroupJobsAndCounts(gi.jgObj, gi.platformGroupEl);
|
||||
} else {
|
||||
gi.grpCountList.empty();
|
||||
gi.jgObj.groupState = "expanded";
|
||||
addJobBtnEls(gi.jgObj, gi.grpJobList);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var togglePinJobCb = function(ev, el, job){
|
||||
$rootScope.$emit(thEvents.jobPin, job);
|
||||
};
|
||||
|
||||
var addJobBtnEls = function(
|
||||
jgObj, jobBtnInterpolator, jobTdEl){
|
||||
|
||||
var jobsShown = 0;
|
||||
|
||||
var lastJobSelected = ThResultSetStore.getSelectedJob(
|
||||
$rootScope.repoName
|
||||
);
|
||||
|
||||
var hText, key, resultState, job, jobStatus, jobBtn, l;
|
||||
var addJobBtnEls = function(jgObj, jobList) {
|
||||
var lastJobSelected = ThResultSetStore.getSelectedJob($rootScope.repoName);
|
||||
var job, l;
|
||||
var jobBtnArray = [];
|
||||
jobList.empty();
|
||||
|
||||
for(l=0; l<jgObj.jobs.length; l++){
|
||||
|
||||
job = jgObj.jobs[l];
|
||||
|
||||
//Set the resultState
|
||||
resultState = thResultStatus(job);
|
||||
|
||||
job.searchStr = thJobSearchStr(job) + ' ' + job.ref_data_name + ' ' +
|
||||
job.signature;
|
||||
|
||||
//Make sure that filtering doesn't effect the resultset counts
|
||||
//displayed
|
||||
if(thJobFilters.showJob(job) === false){
|
||||
//Keep track of visibility with this property. This
|
||||
//way down stream job consumers don't need to repeatedly
|
||||
//call showJob
|
||||
job.visible = false;
|
||||
// Keep track of visibility with this property. This
|
||||
// way down stream job consumers don't need to repeatedly
|
||||
// call showJob
|
||||
job.visible = thJobFilters.showJob(job);
|
||||
|
||||
addJobBtnToArray(job, lastJobSelected, jobBtnArray);
|
||||
}
|
||||
jobList.append(jobBtnArray);
|
||||
};
|
||||
|
||||
var addJobBtnToArray = function(job, lastJobSelected, jobBtnArray) {
|
||||
var jobStatus, jobBtn;
|
||||
|
||||
jobStatus = thResultStatusInfo(thResultStatus(job), job.failure_classification_id);
|
||||
jobStatus.key = getJobMapKey(job);
|
||||
jobStatus.value = job.job_type_symbol;
|
||||
jobStatus.title = getHoverText(job);
|
||||
jobBtn = $(jobBtnInterpolator(jobStatus));
|
||||
|
||||
// If the job is currently selected make sure to re-apply
|
||||
// the job selection styles
|
||||
if( !_.isEmpty(lastJobSelected.job) &&
|
||||
(lastJobSelected.job.id === job.id)){
|
||||
|
||||
setSelectJobStyles(jobBtn);
|
||||
|
||||
//Update the selected job element to the current one
|
||||
ThResultSetStore.setSelectedJob($rootScope.repoName, jobBtn, job);
|
||||
}
|
||||
showHideElement(jobBtn, job.visible);
|
||||
|
||||
jobBtnArray.push(jobBtn);
|
||||
// add a zero-width space between spans so they can wrap
|
||||
jobBtnArray.push(' ');
|
||||
};
|
||||
|
||||
var getGroupInfo = function(el, groupMap) {
|
||||
var gi = {};
|
||||
try {
|
||||
gi.platformGroupEl = $(el).closest(".platform-group");
|
||||
gi.grpJobList = gi.platformGroupEl.find(".group-job-list");
|
||||
gi.grpCountList = gi.platformGroupEl.find(".group-count-list");
|
||||
gi.key = gi.platformGroupEl.find(".job-group").attr(groupKeyAttr);
|
||||
gi.jgObj = groupMap[gi.key].grp_obj;
|
||||
return gi;
|
||||
} catch(TypeError) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Group non-failed jobs as '+n' counts in the UI by default,
|
||||
* and failed jobs as individual buttons.
|
||||
* Each job receives a corresponding resultState which determines its
|
||||
* display.
|
||||
*/
|
||||
var addGroupJobsAndCounts = function(jgObj, platformGroup) {
|
||||
var ct, job, jobCountBtn, l;
|
||||
var countAdded = false;
|
||||
var jobCountBtnArray = [];
|
||||
var jobBtnArray = [];
|
||||
var stateCounts = {};
|
||||
var lastJobSelected = ThResultSetStore.getSelectedJob($rootScope.repoName);
|
||||
|
||||
var jobList = platformGroup.find(".group-job-list");
|
||||
var countList = platformGroup.find(".group-count-list");
|
||||
jobList.empty();
|
||||
countList.empty();
|
||||
|
||||
for (l = 0; l < jgObj.jobs.length; l++) {
|
||||
|
||||
job = jgObj.jobs[l];
|
||||
job.searchStr = thJobSearchStr(job) + ' ' + job.ref_data_name + ' ' +
|
||||
job.signature;
|
||||
|
||||
//Set the resultState
|
||||
var resultStatus = thResultStatus(job);
|
||||
var countInfo = thResultStatusInfo(resultStatus,
|
||||
job.failure_classification_id);
|
||||
|
||||
job.visible = thJobFilters.showJob(job);
|
||||
|
||||
// Even if a job is not visible, add it to the DOM as hidden. This is
|
||||
// important because it can still be "selected" when not visible
|
||||
// or filtered out (like in the case of unclassified failures).
|
||||
//
|
||||
// We don't add it to group counts, because it should not be counted
|
||||
// when filtered out. Failures don't get included in counts anyway.
|
||||
if (_.contains(failResults, resultStatus)) {
|
||||
// render the job itself, not a count
|
||||
addJobBtnToArray(job, lastJobSelected, jobBtnArray);
|
||||
} else {
|
||||
jobsShown++;
|
||||
job.visible = true;
|
||||
}
|
||||
if (job.visible) {
|
||||
_.extend(countInfo, stateCounts[countInfo.btnClass]);
|
||||
if (!_.isEmpty(lastJobSelected.job) &&
|
||||
(lastJobSelected.job.id === job.id)) {
|
||||
// these classes are applied in the interpolator
|
||||
// to designate this count as having one of its
|
||||
// jobs selected.
|
||||
countInfo.selectedClasses = selectedCountCls + " " + largeBtnCls;
|
||||
}
|
||||
|
||||
hText = getHoverText(job);
|
||||
key = getJobMapKey(job);
|
||||
|
||||
jobStatus = thResultStatusInfo(resultState);
|
||||
|
||||
//Add a visual indicator for a failure classification
|
||||
jobStatus.key = key;
|
||||
if(parseInt(job.failure_classification_id, 10) > 1){
|
||||
jobStatus.value = job.job_type_symbol + '*';
|
||||
if (jobStatus.btnClassClassified) {
|
||||
// For result types that are displayed more prominently
|
||||
// when unclassified, switch to the more subtle classified
|
||||
// style.
|
||||
jobStatus.btnClass = jobStatus.btnClassClassified;
|
||||
ct = _.get(_.get(stateCounts, countInfo.btnClass, countInfo),
|
||||
"count", 0);
|
||||
countInfo.count = ct + 1;
|
||||
// keep a reference to the job. If there ends up being
|
||||
// only one for this status, then just add the job itself
|
||||
// rather than a count.
|
||||
countInfo.lastJob = job;
|
||||
stateCounts[countInfo.btnClass] = countInfo;
|
||||
}
|
||||
} else {
|
||||
jobStatus.value = job.job_type_symbol;
|
||||
}
|
||||
|
||||
jobStatus.title = hText;
|
||||
|
||||
jobBtn = $( jobBtnInterpolator(jobStatus));
|
||||
jobBtnArray.push(jobBtn);
|
||||
// add a zero-width space between spans so they can wrap
|
||||
jobBtnArray.push(' ');
|
||||
|
||||
showHideJob(jobBtn, job.visible);
|
||||
|
||||
//If the job is currently selected make sure to re-apply
|
||||
//the job selection styles
|
||||
if( !_.isEmpty(lastJobSelected.job) &&
|
||||
(lastJobSelected.job.id === job.id)){
|
||||
|
||||
setSelectJobStyles(jobBtn);
|
||||
|
||||
//Update the selected job element to the current one
|
||||
ThResultSetStore.setSelectedJob(
|
||||
$rootScope.repoName, jobBtn, job);
|
||||
}
|
||||
}
|
||||
jobTdEl.append(jobBtnArray);
|
||||
|
||||
return jobsShown;
|
||||
_.forEach(stateCounts, function(countInfo) {
|
||||
if (countInfo.count === 1) {
|
||||
// if there is only 1 job for this status, then just add
|
||||
// the job, rather than the count
|
||||
addJobBtnToArray(countInfo.lastJob, lastJobSelected, jobBtnArray);
|
||||
} else {
|
||||
// with more than 1 job for the status, add it as a count
|
||||
countAdded = true;
|
||||
countInfo.value = countInfo.count;
|
||||
countInfo.title = countInfo.count + " " + countInfo.countText + " jobs in group";
|
||||
countInfo.btnClass = countInfo.btnClass + "-count";
|
||||
jobCountBtn = $(jobGroupCountInterpolator(countInfo));
|
||||
jobCountBtnArray.push(jobCountBtn);
|
||||
jobCountBtnArray.push(' ');
|
||||
showHideElement(jobCountBtn, true);
|
||||
}
|
||||
});
|
||||
|
||||
jobList.append(jobBtnArray);
|
||||
|
||||
if (countAdded) {
|
||||
countList.append(jobCountBtnArray);
|
||||
}
|
||||
};
|
||||
|
||||
var jobMouseDown = function(resultset, ev){
|
||||
|
@ -292,6 +396,8 @@ treeherder.directive('thCloneJobs', [
|
|||
|
||||
ThResultSetStore.setSelectedJob($rootScope.repoName, el, job);
|
||||
|
||||
} else {
|
||||
_.bind(clickGroupCb, this, el)();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -308,7 +414,7 @@ treeherder.directive('thCloneJobs', [
|
|||
|
||||
var revision, revisionHtml, userTokens, i;
|
||||
|
||||
for(i=0; i<resultset.revisions.length; i++){
|
||||
for (i=0; i<resultset.revisions.length; i++) {
|
||||
|
||||
revision = resultset.revisions[i];
|
||||
|
||||
|
@ -337,7 +443,7 @@ treeherder.directive('thCloneJobs', [
|
|||
}
|
||||
};
|
||||
|
||||
var toggleRevisions = function(element, expand){
|
||||
var toggleRevisions = function(element, expand) {
|
||||
|
||||
var revisionsEl = element.find('ul').parent();
|
||||
var jobsEl = element.find('table').parent();
|
||||
|
@ -353,13 +459,13 @@ treeherder.directive('thCloneJobs', [
|
|||
var rowEl = revisionsEl.parent();
|
||||
rowEl.css('display', 'block');
|
||||
|
||||
if(on) {
|
||||
if (on) {
|
||||
|
||||
ThResultSetStore.loadRevisions(
|
||||
$rootScope.repoName, this.resultset.id
|
||||
);
|
||||
|
||||
if(jobsElDisplayState === 'block'){
|
||||
if (jobsElDisplayState === 'block') {
|
||||
toggleRevisionsSpanOnWithJobs(revisionsEl);
|
||||
//Make sure the jobs span has correct styles
|
||||
toggleJobsSpanOnWithRevisions(jobsEl);
|
||||
|
@ -372,22 +478,22 @@ treeherder.directive('thCloneJobs', [
|
|||
|
||||
};
|
||||
|
||||
var toggleRevisionsSpanOnWithJobs = function(el){
|
||||
var toggleRevisionsSpanOnWithJobs = function(el) {
|
||||
el.css('display', 'block');
|
||||
el.addClass(col5Cls);
|
||||
};
|
||||
var toggleRevisionsSpanOff = function(el){
|
||||
var toggleRevisionsSpanOff = function(el) {
|
||||
el.css('display', 'none');
|
||||
el.removeClass(col5Cls);
|
||||
};
|
||||
var toggleJobsSpanOnWithRevisions = function(el){
|
||||
var toggleJobsSpanOnWithRevisions = function(el) {
|
||||
el.css('display', 'block');
|
||||
el.removeClass(jobListNoPadCls);
|
||||
el.removeClass(col12Cls);
|
||||
el.addClass(col7Cls);
|
||||
el.addClass(jobListPadCls);
|
||||
};
|
||||
var toggleJobsSpanOnWithoutRevisions = function(el){
|
||||
var toggleJobsSpanOnWithoutRevisions = function(el) {
|
||||
el.css('display', 'block');
|
||||
el.removeClass(col7Cls);
|
||||
el.removeClass(jobListPadCls);
|
||||
|
@ -400,29 +506,26 @@ treeherder.directive('thCloneJobs', [
|
|||
//Empty the job column before populating it
|
||||
jobTdEl.empty();
|
||||
|
||||
var jgObj, jobGroup, jobsShown, i;
|
||||
for(i=0; i<jobGroups.length; i++){
|
||||
var jgObj, jobGroup, i;
|
||||
for (i=0; i<jobGroups.length; i++) {
|
||||
|
||||
jgObj = jobGroups[i];
|
||||
|
||||
jobsShown = 0;
|
||||
if(jgObj.symbol !== '?'){
|
||||
if (jgObj.symbol !== '?') {
|
||||
// Job group detected, add job group symbols
|
||||
jobGroup = $( jobGroupInterpolator(jobGroups[i]) );
|
||||
|
||||
jobGroups[i].grkey = jgObj.mapKey;
|
||||
jobGroups[i].collapsed = true;
|
||||
jobGroup = $(jobGroupInterpolator(jobGroups[i]));
|
||||
jobTdEl.append(jobGroup);
|
||||
|
||||
if (isGroupExpanded(jgObj)) {
|
||||
addJobBtnEls(jgObj, jobGroup.find(".group-job-list"));
|
||||
} else {
|
||||
addGroupJobsAndCounts(jgObj, jobGroup);
|
||||
}
|
||||
} else {
|
||||
// Add the job btn spans
|
||||
jobsShown = addJobBtnEls(
|
||||
jgObj, jobBtnInterpolator, jobGroup.find(".job-group-list"));
|
||||
jobGroup.css("display", jobsShown? "inline": "none");
|
||||
|
||||
}else{
|
||||
|
||||
// Add the job btn spans
|
||||
jobsShown = addJobBtnEls(
|
||||
jgObj, jobBtnInterpolator, jobTdEl);
|
||||
|
||||
addJobBtnEls(jgObj, jobTdEl);
|
||||
}
|
||||
}
|
||||
row.append(jobTdEl);
|
||||
|
@ -446,10 +549,11 @@ treeherder.directive('thCloneJobs', [
|
|||
job = jobMap[jmKey].job_obj;
|
||||
show = thJobFilters.showJob(job);
|
||||
job.visible = show;
|
||||
|
||||
showHideJob($(this), show);
|
||||
showHideElement($(this), show);
|
||||
});
|
||||
|
||||
renderGroups(element, false);
|
||||
|
||||
// hide platforms and groups where all jobs are hidden
|
||||
element.find(".platform").each(function internalFilterPlatform() {
|
||||
var platform = $(this.parentNode);
|
||||
|
@ -457,7 +561,52 @@ treeherder.directive('thCloneJobs', [
|
|||
});
|
||||
|
||||
};
|
||||
var showHideJob = function(job, show) {
|
||||
|
||||
var isGroupExpanded = function(group) {
|
||||
var singleGroupState = group.groupState || $scope.groupState;
|
||||
return singleGroupState === "expanded";
|
||||
};
|
||||
|
||||
/**
|
||||
* Render all the job groups for a resultset. Make decisions on whether
|
||||
* to render all the jobs in the group, or to collapse them as counts.
|
||||
*
|
||||
* If ``resetGroupState`` is set to true, then clear the ``groupState``
|
||||
* for each group that may have been set when a user clicked on it.
|
||||
* If false, then honor the choice to expand or collapse an individual
|
||||
* group and ignore the global setting.
|
||||
*
|
||||
* @param element The resultset for which to render the groups.
|
||||
* @param resetGroupState Whether to reset groups individual expanded
|
||||
* or collapsed states.
|
||||
*/
|
||||
var renderGroups = function(element, resetGroupState) {
|
||||
var groupMap = ThResultSetStore.getGroupMap($rootScope.repoName);
|
||||
// with items in the group, it's not as simple as just hiding or
|
||||
// showing a job or count. Since there can be lots of criteria for whether to show
|
||||
// or hide a job, and any job hidden or shown will change the counts,
|
||||
// the counts must be re-created each time.
|
||||
element.find(".group-job-list").each(function internalFilterGroup(idx, el) {
|
||||
var gi = getGroupInfo(el, groupMap);
|
||||
gi.grpJobList.empty();
|
||||
gi.grpCountList.empty();
|
||||
|
||||
if (resetGroupState) {
|
||||
delete gi.jgObj.groupState;
|
||||
}
|
||||
|
||||
if (isGroupExpanded(gi.jgObj)) {
|
||||
addJobBtnEls(gi.jgObj, gi.platformGroupEl.find(".group-job-list"));
|
||||
} else {
|
||||
addGroupJobsAndCounts(gi.jgObj, gi.platformGroupEl);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Can be used to show/hide a job or a count of jobs
|
||||
*/
|
||||
var showHideElement = function(el, show) {
|
||||
// Note: I was using
|
||||
// jobEl.style.display = "inline";
|
||||
// jobEl.className += " filter-shown";
|
||||
|
@ -468,9 +617,9 @@ treeherder.directive('thCloneJobs', [
|
|||
//
|
||||
// It would be great to be able to do this without adding/removing a class
|
||||
if (show) {
|
||||
job[0].classList.add("filter-shown");
|
||||
el[0].classList.add("filter-shown");
|
||||
} else {
|
||||
job[0].classList.remove("filter-shown");
|
||||
el[0].classList.remove("filter-shown");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -482,7 +631,7 @@ treeherder.directive('thCloneJobs', [
|
|||
platform[0].style.display ="table-row";
|
||||
platform.find(".platform-group").each(function internalFilterGroup() {
|
||||
var grp = $(this);
|
||||
showGrp = grp.find('.job-group-list .filter-shown').length !== 0;
|
||||
showGrp = grp.find('.group-job-list .filter-shown, .group-count-list .filter-shown').length !== 0;
|
||||
grp[0].style.display = showGrp ? "inline" : "none";
|
||||
});
|
||||
|
||||
|
@ -491,7 +640,7 @@ treeherder.directive('thCloneJobs', [
|
|||
}
|
||||
};
|
||||
|
||||
var appendPlatformRow = function(tableEl, rowEl, platformName){
|
||||
var appendPlatformRow = function(tableEl, rowEl, platformName) {
|
||||
|
||||
var tableRows = $(tableEl).find('tr');
|
||||
|
||||
|
@ -529,7 +678,7 @@ treeherder.directive('thCloneJobs', [
|
|||
};
|
||||
|
||||
var updateJobs = function(platformData){
|
||||
angular.forEach(platformData, function(value, platformId){
|
||||
angular.forEach(platformData, function(value, platformId) {
|
||||
|
||||
if(value.resultsetId !== this.resultset.id){
|
||||
//Confirm we are the correct result set
|
||||
|
@ -558,9 +707,9 @@ treeherder.directive('thCloneJobs', [
|
|||
option = value.platformOption;
|
||||
|
||||
//Add platforms
|
||||
platformTdEl = $( platformInterpolator(
|
||||
{'name':platformName, 'option':option, 'id':platformId }
|
||||
) );
|
||||
platformTdEl = $(platformInterpolator(
|
||||
{'name':platformName, 'option':option, 'id':platformId}
|
||||
));
|
||||
|
||||
rowEl.append(platformTdEl);
|
||||
|
||||
|
@ -581,10 +730,12 @@ treeherder.directive('thCloneJobs', [
|
|||
}, this);
|
||||
};
|
||||
|
||||
var scrollToElement = function(el){
|
||||
|
||||
if(el.position() !== undefined){
|
||||
$('.th-global-content').scrollTo(el, 100, {offset: -40});
|
||||
var scrollToElement = function(el, duration) {
|
||||
if (_.isUndefined(duration)) {
|
||||
duration = 50;
|
||||
}
|
||||
if (el.position() !== undefined) {
|
||||
$('.th-global-content').scrollTo(el, duration, {offset: -40});
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -618,6 +769,12 @@ treeherder.directive('thCloneJobs', [
|
|||
_.bind(filterJobs, scope, element)();
|
||||
});
|
||||
|
||||
$rootScope.$on(
|
||||
thEvents.groupStateChanged, function(ev, filterData){
|
||||
_.bind(renderGroups, scope, element, true)();
|
||||
scrollToElement($(viewContentSel).find(".selected-job, .selected-count"), 1);
|
||||
});
|
||||
|
||||
$rootScope.$on(
|
||||
thEvents.searchPage, function(ev, searchData){
|
||||
_.bind(filterJobs, scope, element)();
|
||||
|
@ -637,7 +794,7 @@ treeherder.directive('thCloneJobs', [
|
|||
for(jid in pinnedJobs.jobs){
|
||||
if (pinnedJobs.jobs.hasOwnProperty(jid)) {
|
||||
//Only update the target resultset id
|
||||
if(pinnedJobs.jobs[jid].result_set_id === scope.resultset.id){
|
||||
if (pinnedJobs.jobs[jid].result_set_id === scope.resultset.id) {
|
||||
ThResultSetStore.aggregateJobPlatform(
|
||||
$rootScope.repoName, pinnedJobs.jobs[jid], platformData
|
||||
);
|
||||
|
@ -674,8 +831,7 @@ treeherder.directive('thCloneJobs', [
|
|||
});
|
||||
};
|
||||
|
||||
var generateJobElements = function(
|
||||
resultsetAggregateId, resultset){
|
||||
var generateJobElements = function(resultsetAggregateId, resultset) {
|
||||
|
||||
var tableEl = $('#' + resultsetAggregateId);
|
||||
|
||||
|
@ -683,7 +839,7 @@ treeherder.directive('thCloneJobs', [
|
|||
$(waitSpanEl).css('display', 'none');
|
||||
|
||||
var name, option, platformId, platformKey, row, platformTd, jobTdEl, j;
|
||||
for(j=0; j<resultset.platforms.length; j++){
|
||||
for (j=0; j<resultset.platforms.length; j++) {
|
||||
|
||||
platformId = thAggregateIds.getPlatformRowId(
|
||||
$rootScope.repoName,
|
||||
|
@ -734,7 +890,10 @@ treeherder.directive('thCloneJobs', [
|
|||
}
|
||||
};
|
||||
|
||||
var linker = function(scope, element, attrs){
|
||||
var $scope = null;
|
||||
var linker = function(scope, element, attrs) {
|
||||
|
||||
$scope = scope;
|
||||
|
||||
//Remove any jquery on() bindings
|
||||
element.off();
|
||||
|
@ -757,10 +916,10 @@ treeherder.directive('thCloneJobs', [
|
|||
|
||||
element.append(targetEl);
|
||||
|
||||
if(scope.resultset.platforms !== undefined){
|
||||
if (scope.resultset.platforms !== undefined) {
|
||||
generateJobElements(
|
||||
resultsetAggregateId, scope.resultset);
|
||||
}else{
|
||||
} else {
|
||||
// Hide the job wait span, resultset has no jobs
|
||||
var tableEl = $('#' + resultsetAggregateId);
|
||||
var waitSpanEl = $(tableEl).prev();
|
||||
|
|
|
@ -217,6 +217,7 @@ treeherder.factory('ThResultSetStore', [
|
|||
// maps to help finding objects to update/add
|
||||
rsMap:{},
|
||||
jobMap:{},
|
||||
grpMap:{},
|
||||
unclassifiedFailureMap: {},
|
||||
//used as the offset in paging
|
||||
rsMapOldestTimestamp:null,
|
||||
|
@ -255,11 +256,6 @@ treeherder.factory('ThResultSetStore', [
|
|||
return shownJobs;
|
||||
};
|
||||
|
||||
var getJobMapKey = function(job) {
|
||||
//Build string key for jobMap entires
|
||||
return 'key' + job.id;
|
||||
};
|
||||
|
||||
var getSelectedJob = function(repoName){
|
||||
return { el:repositories[repoName].lastJobElSelected,
|
||||
job:repositories[repoName].lastJobObjSelected };
|
||||
|
@ -330,6 +326,7 @@ treeherder.factory('ThResultSetStore', [
|
|||
// groups
|
||||
for (var gp_i = 0; gp_i < pl_obj.groups.length; gp_i++) {
|
||||
var gr_obj = pl_obj.groups[gp_i];
|
||||
gr_obj.mapKey = thAggregateIds.getGroupMapKey(rs_obj.id, gr_obj.name, gr_obj.symbol, pl_obj.name, pl_obj.option);
|
||||
|
||||
var grMapElement = {
|
||||
grp_obj: gr_obj,
|
||||
|
@ -338,10 +335,21 @@ treeherder.factory('ThResultSetStore', [
|
|||
};
|
||||
plMapElement.groups[gr_obj.name] = grMapElement;
|
||||
|
||||
// check if we need to copy groupState from an existing group
|
||||
// object. This would be set if a user explicitly clicked
|
||||
// a group to toggle it expanded/collapsed.
|
||||
// This value will have been overwritten by the _.extend
|
||||
// in mapResultSetJobs.
|
||||
var oldGroup = repositories[repoName].grpMap[gr_obj.mapKey];
|
||||
if (oldGroup) {
|
||||
gr_obj.groupState = oldGroup.grp_obj.groupState;
|
||||
}
|
||||
repositories[repoName].grpMap[gr_obj.mapKey] = grMapElement;
|
||||
|
||||
// jobs
|
||||
for (var j_i = 0; j_i < gr_obj.jobs.length; j_i++) {
|
||||
var job_obj = gr_obj.jobs[j_i];
|
||||
var key = getJobMapKey(job_obj);
|
||||
var key = thAggregateIds.getJobMapKey(job_obj);
|
||||
|
||||
var jobMapElement = {
|
||||
job_obj: job_obj,
|
||||
|
@ -442,6 +450,7 @@ treeherder.factory('ThResultSetStore', [
|
|||
var grp_obj = {
|
||||
symbol: groupInfo.symbol,
|
||||
name: groupInfo.name,
|
||||
mapKey: groupInfo.mapKey,
|
||||
jobs: []
|
||||
};
|
||||
|
||||
|
@ -620,7 +629,7 @@ treeherder.factory('ThResultSetStore', [
|
|||
*/
|
||||
var updateJob = function(repoName, newJob) {
|
||||
|
||||
var key = getJobMapKey(newJob);
|
||||
var key = thAggregateIds.getJobMapKey(newJob);
|
||||
var loadedJobMap = repositories[repoName].jobMap[key];
|
||||
var loadedJob = loadedJobMap? loadedJobMap.job_obj: null;
|
||||
var rsMapElement = repositories[repoName].rsMap[newJob.result_set_id];
|
||||
|
@ -785,6 +794,9 @@ treeherder.factory('ThResultSetStore', [
|
|||
// this is a "watchable" for jobs
|
||||
return repositories[repoName].jobMap;
|
||||
};
|
||||
var getGroupMap = function(repoName){
|
||||
return repositories[repoName].grpMap;
|
||||
};
|
||||
var getLoadingStatus = function(repoName){
|
||||
return repositories[repoName].loadingStatus;
|
||||
};
|
||||
|
@ -896,6 +908,8 @@ treeherder.factory('ThResultSetStore', [
|
|||
|
||||
var name = job.job_group_name;
|
||||
var symbol = job.job_group_symbol;
|
||||
var mapKey = thAggregateIds.getGroupMapKey(job.result_set_id, name, job.platform, job.platform_option);
|
||||
|
||||
if (job.tier && job.tier !== 1) {
|
||||
if (symbol === "?") {
|
||||
symbol = "";
|
||||
|
@ -905,7 +919,7 @@ treeherder.factory('ThResultSetStore', [
|
|||
symbol = tierLabel;
|
||||
}
|
||||
|
||||
return {name: name, symbol: symbol};
|
||||
return {name: name, symbol: symbol, mapKey: mapKey};
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1034,6 +1048,7 @@ treeherder.factory('ThResultSetStore', [
|
|||
fetchResultSets: fetchResultSets,
|
||||
getAllShownJobs: getAllShownJobs,
|
||||
getJobMap: getJobMap,
|
||||
getGroupMap: getGroupMap,
|
||||
getLoadingStatus: getLoadingStatus,
|
||||
getPlatformKey: getPlatformKey,
|
||||
getResultSet: getResultSet,
|
||||
|
|
|
@ -64,7 +64,7 @@ treeherder.provider('thResultStatusObject', function() {
|
|||
|
||||
treeherder.provider('thResultStatusInfo', function() {
|
||||
this.$get = function() {
|
||||
return function(resultState) {
|
||||
return function(resultState, failure_classification_id) {
|
||||
// default if there is no match, used for pending
|
||||
var resultStatusInfo = {
|
||||
severity: 100,
|
||||
|
@ -77,7 +77,6 @@ treeherder.provider('thResultStatusInfo', function() {
|
|||
resultStatusInfo = {
|
||||
severity: 1,
|
||||
btnClass: "btn-red",
|
||||
btnClassClassified: "btn-red-classified",
|
||||
jobButtonIcon: "glyphicon glyphicon-fire",
|
||||
countText: "busted"
|
||||
};
|
||||
|
@ -86,7 +85,6 @@ treeherder.provider('thResultStatusInfo', function() {
|
|||
resultStatusInfo = {
|
||||
severity: 2,
|
||||
btnClass: "btn-purple",
|
||||
btnClassClassified: "btn-purple-classified",
|
||||
jobButtonIcon: "glyphicon glyphicon-fire",
|
||||
countText: "exception"
|
||||
};
|
||||
|
@ -95,7 +93,6 @@ treeherder.provider('thResultStatusInfo', function() {
|
|||
resultStatusInfo = {
|
||||
severity: 3,
|
||||
btnClass: "btn-orange",
|
||||
btnClassClassified: "btn-orange-classified",
|
||||
jobButtonIcon: "glyphicon glyphicon-warning-sign",
|
||||
countText: "failed"
|
||||
};
|
||||
|
@ -104,7 +101,6 @@ treeherder.provider('thResultStatusInfo', function() {
|
|||
resultStatusInfo = {
|
||||
severity: 4,
|
||||
btnClass: "btn-black",
|
||||
btnClassClassified: "btn-black-classified",
|
||||
jobButtonIcon: "",
|
||||
countText: "unknown"
|
||||
};
|
||||
|
@ -159,6 +155,11 @@ treeherder.provider('thResultStatusInfo', function() {
|
|||
break;
|
||||
}
|
||||
|
||||
// handle if a job is classified
|
||||
if(parseInt(failure_classification_id, 10) > 1){
|
||||
resultStatusInfo.btnClass = resultStatusInfo.btnClass + "-classified";
|
||||
resultStatusInfo.countText = "classified " + resultStatusInfo.countText;
|
||||
}
|
||||
return resultStatusInfo;
|
||||
};
|
||||
|
||||
|
@ -207,6 +208,8 @@ treeherder.provider('thEvents', function() {
|
|||
// fired when a global filter has changed
|
||||
globalFilterChanged: "status-filter-changed-EVT",
|
||||
|
||||
groupStateChanged: "group-state-changed-EVT",
|
||||
|
||||
toggleRevisions: "toggle-revisions-EVT",
|
||||
|
||||
toggleAllRevisions: "toggle-all-revisions-EVT",
|
||||
|
@ -257,10 +260,23 @@ treeherder.provider('thAggregateIds', function() {
|
|||
return escape(repoName + resultsetId + revision);
|
||||
};
|
||||
|
||||
var getGroupMapKey = function(result_set_id, grName, grSymbol, plName, plOpt) {
|
||||
//Build string key for groupMap entires
|
||||
return escape(result_set_id + grName + grSymbol + plName + plOpt);
|
||||
};
|
||||
|
||||
var getJobMapKey = function(job) {
|
||||
//Build string key for jobMap entires
|
||||
return 'key' + job.id;
|
||||
};
|
||||
|
||||
this.$get = function() {
|
||||
return {
|
||||
getPlatformRowId:getPlatformRowId,
|
||||
getResultsetTableId:getResultsetTableId
|
||||
getResultsetTableId:getResultsetTableId,
|
||||
getJobMapKey: getJobMapKey,
|
||||
getGroupMapKey: getGroupMapKey,
|
||||
escape: escape
|
||||
};
|
||||
};
|
||||
});
|
||||
|
|
|
@ -42,7 +42,8 @@ treeherder.factory('thCloneHtml', [
|
|||
'resultsetClone.html',
|
||||
'platformClone.html',
|
||||
'jobTdClone.html',
|
||||
'jobGroupBeginClone.html',
|
||||
'jobGroupClone.html',
|
||||
'jobGroupCountClone.html',
|
||||
'jobBtnClone.html',
|
||||
'revisionUrlClone.html',
|
||||
'pushlogRevisionsClone.html'
|
||||
|
|
|
@ -162,11 +162,11 @@ treeherder.value("thJobNavSelectors",
|
|||
{
|
||||
ALL_JOBS: {
|
||||
name: "jobs",
|
||||
selector: ".job-btn"
|
||||
selector: ".job-btn, .selected-job, .selected-count"
|
||||
},
|
||||
UNCLASSIFIED_FAILURES: {
|
||||
name: "unclassified failures",
|
||||
selector: ".selected-job, " +
|
||||
selector: ".selected-job, .selected-count, " +
|
||||
".job-btn.btn-red, " +
|
||||
".job-btn.btn-orange, " +
|
||||
".job-btn.btn-purple"
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
<span class="btn btn-sm btn-resultset"
|
||||
tabindex="0" role="button"
|
||||
title="Pin all visible jobs in this resultset"
|
||||
title="Pin all available jobs in this resultset"
|
||||
ignore-job-clear-on-click
|
||||
ng-click="pinAllShownJobs()">
|
||||
<span class="glyphicon glyphicon-pushpin"
|
||||
|
|
|
@ -39,6 +39,20 @@
|
|||
</span>
|
||||
</span>
|
||||
|
||||
<!--Toggle Group State Button-->
|
||||
<span class="btn-group">
|
||||
<span class="btn btn-view-nav btn-sm btn-toggle-group-state"
|
||||
tabindex="0" role="button"
|
||||
ng-click="toggleGroupState()">(
|
||||
<span ng-if="groupState==='collapsed'"
|
||||
class="group-state-nav-icon"
|
||||
title="Expand job groups">+</span>
|
||||
<span ng-if="groupState!=='collapsed'"
|
||||
class="group-state-nav-icon"
|
||||
title="Collapse job groups">-</span>
|
||||
)</span>
|
||||
</span>
|
||||
|
||||
<!--Quick Filter Field-->
|
||||
<span ng-controller="SearchCtrl" class="form-group form-inline" id="quick-filter-parent">
|
||||
<input id="quick-filter"
|
||||
|
|
Загрузка…
Ссылка в новой задаче