зеркало из https://github.com/mozilla/treeherder.git
Bug 1242905 - Revision list react component (#2029)
Begins migration of job rendering to react by replacing cloned revision lists with a new react component. Related changes: - Prepends the ignore-job-on-click attribute with data- so that react will render it - Makes the linkifyBugs filter wrap its html attributes in quotes consistently - Adds explict whitespace via CSS in a few places that previously depended on whitespace in markup
This commit is contained in:
Родитель
5a2745c2e4
Коммит
676e1f4738
|
@ -13,6 +13,9 @@ module.exports = function (config) {
|
|||
files: [
|
||||
'ui/vendor/angular/angular.js',
|
||||
'ui/vendor/angular/angular-*.js',
|
||||
'tests/ui/vendor/react-with-addons.min.js',
|
||||
'ui/vendor/react/react-dom.min.js',
|
||||
'ui/vendor/ngReact/ngReact.min.js',
|
||||
'ui/vendor/ui-bootstrap-*.js',
|
||||
'ui/vendor/jquery-*.js',
|
||||
'ui/vendor/jquery.ui.effect.js',
|
||||
|
@ -30,6 +33,7 @@ module.exports = function (config) {
|
|||
'ui/js/directives/treeherder/**/*.js',
|
||||
'ui/js/models/**/*.js',
|
||||
'ui/js/services/**/*.js',
|
||||
'ui/js/reactrevisions.js',
|
||||
'ui/plugins/**/*.js',
|
||||
'tests/ui/vendor/jasmine-jquery.js',
|
||||
'tests/ui/unit/**/*.js',
|
||||
|
|
|
@ -35,13 +35,13 @@ describe('linkifyBugs filter', function() {
|
|||
it('linkifies a Bug', function() {
|
||||
var linkifyBugs = $filter('linkifyBugs');
|
||||
expect(linkifyBugs('Bug 123456'))
|
||||
.toEqual('Bug <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=123456" data-bugid=123456 title=bugzilla.mozilla.org>123456</a>');
|
||||
.toEqual('Bug <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=123456" data-bugid="123456" title="bugzilla.mozilla.org">123456</a>');
|
||||
});
|
||||
|
||||
it('linkifies a PR', function() {
|
||||
var linkifyBugs = $filter('linkifyBugs');
|
||||
expect(linkifyBugs('PR#123456'))
|
||||
.toEqual('PR#<a href="https://github.com/mozilla-b2g/gaia/pull/123456" data-prid=123456 title=github.com>123456</a>');
|
||||
.toEqual('PR#<a href="https://github.com/mozilla-b2g/gaia/pull/123456" data-prid="123456" title="github.com">123456</a>');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
'use strict';
|
||||
|
||||
describe('Revision list react component', () => {
|
||||
var $filter;
|
||||
var compile, mockData;
|
||||
beforeEach(module('treeherder'));
|
||||
beforeEach(module('react'));
|
||||
beforeEach(inject((_$filter_) => {
|
||||
$filter = _$filter_;
|
||||
}));
|
||||
beforeEach(inject(() => {
|
||||
var resultset = {
|
||||
"id": 151371,
|
||||
"revision_hash": "0056da58e1efd70711c8f98336eaf866f1aa8936",
|
||||
"revision": "5a110ad242ead60e71d2186bae78b1fb766ad5ff",
|
||||
"revisions_uri": "/api/project/mozilla-inbound/resultset/151371/revisions/",
|
||||
"revision_count": 3,
|
||||
"author": "ryanvm@gmail.com",
|
||||
"push_timestamp": 1481326280,
|
||||
"repository_id": 2,
|
||||
"revisions": [{
|
||||
"result_set_id": 151371,
|
||||
"repository_id": 2,
|
||||
"revision": "5a110ad242ead60e71d2186bae78b1fb766ad5ff",
|
||||
"author":"André Bargull <andre.bargull@gmail.com>",
|
||||
"comments": "Bug 1319926 - Part 2: Collect telemetry about deprecated String generics methods. r=jandem"
|
||||
},{
|
||||
"result_set_id": 151371,
|
||||
"repository_id": 2,
|
||||
"revision": "07d6bf74b7a2552da91b5e2fce0fa0bc3b457394",
|
||||
"author": "André Bargull <andre.bargull@gmail.com>",
|
||||
"comments":"Bug 1319926 - Part 1: Warn when deprecated String generics methods are used. r=jandem"
|
||||
},{
|
||||
"result_set_id": 151371,
|
||||
"repository_id": 2,
|
||||
"revision": "e83eaf2380c65400dc03c6f3615d4b2cef669af3",
|
||||
"author": "Frédéric Wang <fred.wang@free.fr>",
|
||||
"comments": "Bug 1322743 - Add STIX Two Math to the list of math fonts. r=karlt"
|
||||
}]
|
||||
};
|
||||
var repo = {
|
||||
"id": 2,
|
||||
"repository_group": {
|
||||
"name": "development",
|
||||
"description": ""
|
||||
},
|
||||
"name": "mozilla-inbound",
|
||||
"dvcs_type": "hg",
|
||||
"url": "https://hg.mozilla.org/integration/mozilla-inbound",
|
||||
"branch": null,
|
||||
"codebase": "gecko",
|
||||
"description": "",
|
||||
"active_status": "active",
|
||||
"performance_alerts_enabled": true,
|
||||
"pushlogURL": "https://hg.mozilla.org/integration/mozilla-inbound/pushloghtml"
|
||||
};
|
||||
// Mock these simple functions so we don't have to call ThRepositoryModel.load() first to use it
|
||||
repo.getRevisionHref = () => `${repo.url}/rev/${resultset.revision}`;
|
||||
repo.getPushLogHref = (revision) => `${repo.pushlogURL}?changeset=${revision}`;
|
||||
mockData = {
|
||||
resultset,
|
||||
repo
|
||||
};
|
||||
}));
|
||||
|
||||
beforeEach(inject(($rootScope, $timeout, $compile) => {
|
||||
compile = function(el, scope) {
|
||||
var $scope = $rootScope.$new();
|
||||
$scope = _.extend($scope, scope);
|
||||
var compiledEl = $compile(el)($scope);
|
||||
$timeout.flush();
|
||||
$scope.$digest();
|
||||
return compiledEl;
|
||||
};
|
||||
}));
|
||||
|
||||
it('renders the correct number of revisions in a list', () => {
|
||||
var component = compile('<revisions repo="repo" resultset="resultset" />', mockData);
|
||||
var revisionItems = component[0].querySelectorAll('li');
|
||||
expect(revisionItems.length).toEqual(mockData['resultset']['revision_count']);
|
||||
});
|
||||
|
||||
it('renders the linked revision hashes', () => {
|
||||
var component = compile('<revisions repo="repo" resultset="resultset" />', mockData);
|
||||
var links = component[0].querySelectorAll('.revision-holder a');
|
||||
expect(links.length).toEqual(mockData['resultset']['revision_count']);
|
||||
Array.prototype.forEach.call(links, (link, i) => {
|
||||
expect(link.href).toEqual(mockData.repo.getRevisionHref());
|
||||
expect(link.title).toEqual(`Open revision ${mockData.resultset.revisions[i].revision} on ${mockData.repo.url}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the contributors\' initials', () => {
|
||||
var component = compile('<revisions repo="repo" resultset="resultset" />', mockData);
|
||||
var initials = component[0].querySelectorAll('.label.label-initials');
|
||||
expect(initials.length).toEqual(mockData.resultset.revision_count);
|
||||
Array.prototype.forEach.call(initials, (initial, i) => {
|
||||
var revisionData = mockData.resultset.revisions[i];
|
||||
var userTokens = revisionData.author.split(/[<>]+/);
|
||||
var name = userTokens[0];
|
||||
var email = null;
|
||||
if (userTokens.length > 1) email = userTokens[1];
|
||||
var nameString = name;
|
||||
if (email !== null) nameString += `: ${email}`;
|
||||
|
||||
expect(initial.outerHTML).toEqual($filter('initials')(name));
|
||||
expect(initial.parentNode.title).toEqual(nameString);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders an "...and more" link if the revision count is higher than the max display count of 20', () => {
|
||||
mockData.resultset.revision_count = 21;
|
||||
|
||||
var component = compile('<revisions repo="repo" resultset="resultset" />', mockData);
|
||||
var revisionItems = component[0].querySelectorAll('li');
|
||||
expect(revisionItems.length).toEqual(mockData.resultset.revisions.length + 1);
|
||||
|
||||
var lastItem = revisionItems[revisionItems.length - 1];
|
||||
expect(lastItem.textContent).toEqual('\u2026and more');
|
||||
expect(lastItem.querySelector('a i.fa.fa-external-link-square')).toBeDefined();
|
||||
});
|
||||
|
||||
it('linkifies bugs IDs in the comments', () => {
|
||||
var escapedComment = _.escape(mockData.resultset.revisions[0].comments.split('\n')[0]);
|
||||
var linkifiedCommentText = $filter('linkifyBugs')(escapedComment);
|
||||
|
||||
var component = compile('<revisions repo="repo" resultset="resultset" />', mockData);
|
||||
var commentEm = component[0].querySelector('.revision-comment em');
|
||||
expect(commentEm.innerHTML).toEqual(linkifiedCommentText);
|
||||
|
||||
});
|
||||
|
||||
it('marks the revision as backed out if the words "Back/Backed out" appear in the comments', () => {
|
||||
var component, firstRevision;
|
||||
mockData.resultset.revisions[0].comments = "Backed out changeset a6e2d96c1274 (bug 1322565) for eslint failure";
|
||||
|
||||
component = compile('<revisions repo="repo" resultset="resultset" />', mockData);
|
||||
firstRevision = component[0].querySelector('li .revision');
|
||||
expect(firstRevision.getAttribute('data-tags').indexOf('backout')).not.toEqual(-1);
|
||||
|
||||
mockData.resultset.revisions[0].comments = "Back out changeset a6e2d96c1274 (bug 1322565) for eslint failure";
|
||||
component = compile('<revisions repo="repo" resultset="resultset" />', mockData);
|
||||
firstRevision = component[0].querySelector('li .revision');
|
||||
expect(firstRevision.getAttribute('data-tags').indexOf('backout')).not.toEqual(-1);
|
||||
});
|
||||
|
||||
});
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -223,7 +223,7 @@ input:focus::-moz-placeholder {
|
|||
|
||||
.label-initials {
|
||||
display: inline-block;
|
||||
margin-right: 0.2em;
|
||||
margin-right: 0.5em;
|
||||
padding: 0.2em 0.3em;
|
||||
width: 2.5em;
|
||||
border: 1px solid #999999;
|
||||
|
|
|
@ -119,6 +119,10 @@ fieldset[disabled] .btn-resultset:hover {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.revision-list .fa-external-link-square {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.revision {
|
||||
font-size: 12px;
|
||||
padding-top: 2px;
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
</div>
|
||||
|
||||
<th-notification-box></th-notification-box>
|
||||
<script src="vendor/react/react.min.js"></script>
|
||||
<script src="vendor/react/react-dom.min.js"></script>
|
||||
|
||||
<!-- build:js js/index.min.js -->
|
||||
<script src="vendor/jquery-2.1.3.js"></script>
|
||||
|
@ -86,6 +88,7 @@
|
|||
<script src="js/directives/treeherder/resultsets.js"></script>
|
||||
<script src="js/directives/treeherder/top_nav_bar.js"></script>
|
||||
<script src="js/directives/treeherder/bottom_nav_panel.js"></script>
|
||||
<script src="js/reactrevisions.js"></script>
|
||||
<!-- Main services -->
|
||||
<script src="js/services/main.js"></script>
|
||||
<script src="js/services/buildapi.js"></script>
|
||||
|
@ -138,43 +141,14 @@
|
|||
<script src="js/filters.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
<script src="vendor/ngReact/ngReact.min.js"></script>
|
||||
|
||||
<!-- build:dontbuild -->
|
||||
<script src="js/config/local.conf.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
<!-- Clone targets -->
|
||||
|
||||
<!-- Clone target for each revision -->
|
||||
<script type="'text/ng-template'" id="revisionsClone.html">
|
||||
<div class="clearfix"></div>
|
||||
<li>
|
||||
<span class="revision" data-tags="{{comment_tags}}">
|
||||
<span class="revision-holder">
|
||||
<a href="{{currentRepo.getRevisionHref(revision)}}"
|
||||
title="Open revision {{revision}} on {{currentRepo.url}}"
|
||||
ignore-job-clear-on-click>{{revision | limitTo: 12}}</a>
|
||||
</span>
|
||||
<span title="{{name}}: {{email}}">{{name|initials}}</span>
|
||||
<span title="{{escaped_comment}}">
|
||||
<span class="revision-comment">
|
||||
<em>{{escaped_comment_linkified}}</em>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<!-- Clone target for "more" link for large revision sets -->
|
||||
<script type="'text/ng-template'" id="pushlogRevisionsClone.html">
|
||||
<li>
|
||||
<a href="{{currentRepo.getPushLogHref(revision)}}"
|
||||
ignore-job-clear-on-click
|
||||
target="_blank"> ...and more
|
||||
<i class="fa fa-external-link-square"></i>
|
||||
</a>
|
||||
</li>
|
||||
</script>
|
||||
|
||||
<!-- Clone target for each result set -->
|
||||
<script type="'text/ng-template'" id="resultsetClone.html">
|
||||
<div class="clearfix"></div>
|
||||
|
@ -206,7 +180,7 @@
|
|||
<span class="disabled job-group" title="{{ name }}"
|
||||
data-grkey="{{ grkey }}">
|
||||
<button class="btn group-symbol"
|
||||
ignore-job-clear-on-click>{{ symbol }}{{(tier) ?
|
||||
data-ignore-job-clear-on-click>{{ symbol }}{{(tier) ?
|
||||
'<span class="small text-muted">[tier ' + tier + ']</span>': ""}}
|
||||
</button>
|
||||
<span class="group-content">
|
||||
|
@ -231,7 +205,7 @@
|
|||
<script type="'text/ng-template'" id="jobBtnClone.html">
|
||||
<button class="btn job-btn {{ btnClass }} {{ key }} {{ extraClasses }}"
|
||||
data-jmkey="{{ key }}"
|
||||
ignore-job-clear-on-click
|
||||
data-ignore-job-clear-on-click
|
||||
title="{{ title }}">{{ value }}</button>
|
||||
</script>
|
||||
|
||||
|
@ -240,7 +214,7 @@
|
|||
<button class="btn runnable-job-btn {{ key }} {{ extraClasses }}"
|
||||
data-jmkey="{{ key }}"
|
||||
data-buildername="{{ buildername }}"
|
||||
ignore-job-clear-on-click
|
||||
data-ignore-job-clear-on-click
|
||||
title="{{ title }}">{{ value }}</button>
|
||||
</script>
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ treeherderApp.controller('MainCtrl', [
|
|||
$scope.clearJobOnClick = function(event) {
|
||||
var element = event.target;
|
||||
// Suppress for various UI elements so selection is preserved
|
||||
var ignoreClear = element.hasAttribute("ignore-job-clear-on-click");
|
||||
var ignoreClear = element.hasAttribute("data-ignore-job-clear-on-click");
|
||||
|
||||
if (!ignoreClear && !thPinboard.hasPinnedJobs()) {
|
||||
$scope.closeJob();
|
||||
|
|
|
@ -6,13 +6,13 @@ treeherder.directive('thCloneJobs', [
|
|||
'thServiceDomain', 'thResultStatusInfo', 'thEvents', 'thAggregateIds',
|
||||
'thJobFilters', 'thResultStatusObject', 'ThResultSetStore',
|
||||
'ThJobModel', 'linkifyBugsFilter', 'thResultStatus', 'thPlatformName',
|
||||
'thNotify', '$timeout',
|
||||
'thNotify', '$timeout', '$compile',
|
||||
function(
|
||||
$rootScope, $http, ThLog, thUrl, thCloneHtml,
|
||||
thServiceDomain, thResultStatusInfo, thEvents, thAggregateIds,
|
||||
thJobFilters, thResultStatusObject, ThResultSetStore,
|
||||
ThJobModel, linkifyBugsFilter, thResultStatus, thPlatformName,
|
||||
thNotify, $timeout){
|
||||
thNotify, $timeout, $compile){
|
||||
|
||||
var $log = new ThLog("thCloneJobs");
|
||||
|
||||
|
@ -450,54 +450,16 @@ treeherder.directive('thCloneJobs', [
|
|||
/**
|
||||
* Add the list of revisions to the resultset
|
||||
*/
|
||||
var addRevisions = function(resultset, element){
|
||||
var addRevisions = function(scope, element){
|
||||
|
||||
if (resultset.revisions.length > 0){
|
||||
if (scope.resultset.revisions.length > 0){
|
||||
|
||||
var revisionInterpolator = thCloneHtml.get('revisionsClone').interpolator;
|
||||
var ulEl = element.find('.revision-list');
|
||||
|
||||
var ulEl = element.find('ul');
|
||||
_.extend(scope, { repo: $rootScope.currentRepo });
|
||||
var revisionList = $compile('<revisions resultset="resultset" repo="repo"></revisions>')(scope);
|
||||
$(ulEl).replaceWith(revisionList);
|
||||
|
||||
//make sure we're starting with an empty element
|
||||
$(ulEl).empty();
|
||||
|
||||
var revision, revisionHtml, userTokens, i;
|
||||
|
||||
for (i=0; i<resultset.revisions.length; i++) {
|
||||
|
||||
revision = resultset.revisions[i];
|
||||
|
||||
revision.urlBasePath = $rootScope.urlBasePath;
|
||||
revision.currentRepo = $rootScope.currentRepo;
|
||||
|
||||
userTokens = revision.author.split(/[<>]+/);
|
||||
if (userTokens.length > 1) {
|
||||
revision.email = userTokens[1];
|
||||
}
|
||||
revision.name = userTokens[0].trim();
|
||||
// Only use the first line of the full commit message.
|
||||
revision.escaped_comment = _.escape(revision.comments.split('\n')[0]);
|
||||
revision.escaped_comment_linkified = linkifyBugsFilter(revision.escaped_comment);
|
||||
|
||||
// Parse the comment so we can tag things like backouts
|
||||
var tags = "";
|
||||
if (revision.escaped_comment.search("Backed out") >= 0 ||
|
||||
revision.escaped_comment.search("Back out") >= 0) {
|
||||
tags += "backout ";
|
||||
}
|
||||
revision.comment_tags = tags.trim();
|
||||
|
||||
revisionHtml = revisionInterpolator(revision);
|
||||
ulEl.append(revisionHtml);
|
||||
}
|
||||
if (resultset.revision_count > resultset.revisions.length) {
|
||||
|
||||
var pushlogInterpolator = thCloneHtml.get('pushlogRevisionsClone').interpolator;
|
||||
ulEl.append(pushlogInterpolator({
|
||||
currentRepo: $rootScope.currentRepo,
|
||||
revision: resultset.revision
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1052,7 +1014,7 @@ treeherder.directive('thCloneJobs', [
|
|||
tableInterpolator({ aggregateId:resultsetAggregateId })
|
||||
);
|
||||
|
||||
addRevisions(scope.resultset, targetEl);
|
||||
addRevisions(scope, targetEl);
|
||||
|
||||
element.append(targetEl);
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ treeherder.directive('thAuthor', function () {
|
|||
},
|
||||
template: '<span title="View resultsets for {{authorName}}: {{authorEmail}}">' +
|
||||
'<a href="{{authorResultsetFilterUrl}}"' +
|
||||
'ignore-job-clear-on-click>{{authorName}}</a></span>'
|
||||
'data-ignore-job-clear-on-click>{{authorName}}</a></span>'
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -71,10 +71,10 @@ treeherder.filter('linkifyBugs', function() {
|
|||
// Settings
|
||||
var bug_title = 'bugzilla.mozilla.org';
|
||||
var bug_url = '<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=$1" ' +
|
||||
'data-bugid=$1 ' + 'title=' + bug_title + '>$1</a>';
|
||||
'data-bugid="$1" ' + 'title="' + bug_title + '">$1</a>';
|
||||
var pr_title = 'github.com';
|
||||
var pr_url = '<a href="https://github.com/mozilla-b2g/gaia/pull/$1" ' +
|
||||
'data-prid=$1 ' + 'title=' + pr_title + '>$1</a>';
|
||||
'data-prid="$1" ' + 'title="' + pr_title + '">$1</a>';
|
||||
|
||||
if (bug_matches) {
|
||||
// Separate passes to preserve prefix
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
'use strict';
|
||||
|
||||
var moreRevisionsLinkComponent = React.createClass({
|
||||
displayName: 'pushlogRevisionComponent',
|
||||
propTypes: {
|
||||
href: React.PropTypes.string.isRequired
|
||||
},
|
||||
render() {
|
||||
return React.DOM.li(
|
||||
null,
|
||||
React.DOM.a({
|
||||
href: this.props.href,
|
||||
'data-ignore-job-clear-on-click': true,
|
||||
target: '_blank'
|
||||
},
|
||||
'\u2026and more',
|
||||
React.DOM.i({ className: 'fa fa-external-link-square' })
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
var moreRevisionsLinkComponentFactory = React.createFactory(moreRevisionsLinkComponent);
|
||||
|
||||
var revisionItemComponent = React.createClass({
|
||||
displayName: 'revisionItemComponent',
|
||||
propTypes: {
|
||||
revision: React.PropTypes.object.isRequired,
|
||||
repo: React.PropTypes.object.isRequired,
|
||||
linkifyBugsFilter: React.PropTypes.func.isRequired,
|
||||
initialsFilter: React.PropTypes.func.isRequired
|
||||
},
|
||||
render() {
|
||||
var email, name, userTokens, escapedComment, escapedCommentHTML, initialsHTML, tags;
|
||||
|
||||
userTokens = this.props.revision.author.split(/[<>]+/);
|
||||
name = userTokens[0];
|
||||
if (userTokens.length > 1) email = userTokens[1];
|
||||
initialsHTML = { __html: this.props.initialsFilter(name) };
|
||||
|
||||
escapedComment = _.escape(this.props.revision.comments.split('\n')[0]);
|
||||
escapedCommentHTML = { __html: this.props.linkifyBugsFilter(escapedComment) };
|
||||
|
||||
tags = "";
|
||||
if (escapedComment.search("Backed out") >= 0 ||
|
||||
escapedComment.search("Back out") >= 0) {
|
||||
tags += "backout ";
|
||||
}
|
||||
tags = tags.trim();
|
||||
|
||||
return React.DOM.li(
|
||||
{ className: 'clearfix' },
|
||||
React.DOM.span({
|
||||
className: 'revision',
|
||||
'data-tags': tags
|
||||
},
|
||||
React.DOM.span(
|
||||
{ className: 'revision-holder' },
|
||||
React.DOM.a({
|
||||
title: `Open revision ${this.props.revision.revision} on ${this.props.repo.url}`,
|
||||
href: this.props.repo.getRevisionHref(this.props.revision.revision),
|
||||
'data-ignore-job-clear-on-click': true
|
||||
},
|
||||
this.props.revision.revision.substring(0, 12)
|
||||
)),
|
||||
React.DOM.span({
|
||||
title: `${name}: ${email}`,
|
||||
dangerouslySetInnerHTML: initialsHTML
|
||||
}),
|
||||
React.DOM.span(
|
||||
{ title: escapedComment },
|
||||
React.DOM.span(
|
||||
{ className: 'revision-comment' },
|
||||
React.DOM.em({ dangerouslySetInnerHTML: escapedCommentHTML })
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
var revisionItemComponentFactory = React.createFactory(revisionItemComponent);
|
||||
|
||||
var revisionListComponent = React.createClass({
|
||||
displayName: 'revisionListComponent',
|
||||
propTypes: {
|
||||
resultset: React.PropTypes.object.isRequired,
|
||||
repo: React.PropTypes.object.isRequired,
|
||||
$injector: React.PropTypes.object.isRequired
|
||||
},
|
||||
render() {
|
||||
var repo = this.props.repo;
|
||||
// Possible "...and more" link
|
||||
var moreLink = null;
|
||||
|
||||
if (this.props.resultset.revision_count > this.props.resultset.revisions.length) {
|
||||
moreLink = moreRevisionsLinkComponentFactory({
|
||||
href: this.props.repo.getPushLogHref(this.props.resultset.revision)
|
||||
});
|
||||
}
|
||||
var $injector = this.props.$injector;
|
||||
|
||||
return React.DOM.span(
|
||||
{ className: 'revision-list col-xs-5' },
|
||||
React.DOM.ul(
|
||||
{ className: 'list-unstyled' },
|
||||
this.props.resultset.revisions.map(function(item, i) {
|
||||
return revisionItemComponentFactory({
|
||||
initialsFilter: $injector.get('$filter')('initials'),
|
||||
linkifyBugsFilter: $injector.get('$filter')('linkifyBugs'),
|
||||
revision: item,
|
||||
repo: repo,
|
||||
key: i
|
||||
});
|
||||
}),
|
||||
moreLink
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
treeherder.directive('revisions', function (reactDirective, $injector) {
|
||||
return reactDirective(revisionListComponent, undefined, {}, {$injector});
|
||||
});
|
|
@ -47,15 +47,13 @@ treeherder.factory('thCloneHtml', [
|
|||
function($interpolate) {
|
||||
|
||||
var cloneTemplateIds = [
|
||||
'revisionsClone.html',
|
||||
'resultsetClone.html',
|
||||
'platformClone.html',
|
||||
'jobTdClone.html',
|
||||
'jobGroupClone.html',
|
||||
'jobGroupCountClone.html',
|
||||
'jobBtnClone.html',
|
||||
'runnableJobBtnClone.html',
|
||||
'pushlogRevisionsClone.html'
|
||||
'resultsetClone.html',
|
||||
'runnableJobBtnClone.html'
|
||||
];
|
||||
|
||||
var templateId, templateName, templateTxt, i;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
var treeherderApp = angular.module('treeherder.app',
|
||||
['treeherder', 'ui.bootstrap', 'ngRoute',
|
||||
'mc.resizer', 'angular-toArrayFilter']);
|
||||
'mc.resizer', 'angular-toArrayFilter', 'react']);
|
||||
|
||||
treeherderApp.config(function($compileProvider, $routeProvider,
|
||||
$httpProvider, $logProvider, $resourceProvider,
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<span>
|
||||
<a href="{{::revisionResultsetFilterUrl}}"
|
||||
title="View only this resultset"
|
||||
ignore-job-clear-on-click>{{::resultsetDateStr}}
|
||||
data-ignore-job-clear-on-click>{{::resultsetDateStr}}
|
||||
<span class="fa fa-external-link icon-superscript"></span></a> - </span>
|
||||
<th-author author="{{::resultset.author}}"></th-author>
|
||||
</span>
|
||||
|
@ -28,24 +28,24 @@
|
|||
<button class="btn btn-sm btn-resultset cancel-all-jobs-btn"
|
||||
ng-attr-title="{{getCancelJobsTitle(resultset.revision)}}"
|
||||
ng-show="currentRepo.repository_group.name == 'try' || user.is_staff"
|
||||
ignore-job-clear-on-click
|
||||
data-ignore-job-clear-on-click
|
||||
ng-disabled="!canCancelJobs()"
|
||||
ng-click="confirmCancelAllJobs()">
|
||||
<span class="fa fa-times-circle cancel-job-icon dim-quarter"
|
||||
ignore-job-clear-on-click></span>
|
||||
data-ignore-job-clear-on-click></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-resultset pin-all-jobs-btn"
|
||||
title="Pin all available jobs in this resultset"
|
||||
ignore-job-clear-on-click
|
||||
data-ignore-job-clear-on-click
|
||||
ng-click="pinAllShownJobs()">
|
||||
<span class="glyphicon glyphicon-pushpin"
|
||||
ignore-job-clear-on-click></span>
|
||||
data-ignore-job-clear-on-click></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-resultset trigger-new-jobs-btn"
|
||||
title="Trigger new jobs"
|
||||
ng-show="showTriggerButton()"
|
||||
ng-disabled="!user.loggedin"
|
||||
ignore-job-clear-on-click
|
||||
data-ignore-job-clear-on-click
|
||||
ng-click="triggerNewJobs()">
|
||||
Trigger New Jobs
|
||||
</button>
|
||||
|
|
|
@ -5,31 +5,31 @@
|
|||
title="Action menu"
|
||||
data-hover="dropdown"
|
||||
data-delay="1000"
|
||||
ignore-job-clear-on-click>
|
||||
<span class="caret" ignore-job-clear-on-click></span>
|
||||
data-ignore-job-clear-on-click>
|
||||
<span class="caret" data-ignore-job-clear-on-click></span>
|
||||
</button>
|
||||
<!-- Menu contents -->
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li><a target="_blank" ignore-job-clear-on-click
|
||||
<li><a target="_blank" data-ignore-job-clear-on-click
|
||||
title="Add new jobs to this resultset"
|
||||
href=""
|
||||
ng-hide="!user.loggedin || resultset.isRunnableVisible"
|
||||
ng-click="showRunnableJobs()">Add new jobs</a></li>
|
||||
<li><a target="_blank" ignore-job-clear-on-click
|
||||
<li><a target="_blank" data-ignore-job-clear-on-click
|
||||
title="Hide Runnable Jobs"
|
||||
href=""
|
||||
ng-show="resultset.isRunnableVisible"
|
||||
ng-click="deleteRunnableJobs()">Hide Runnable Jobs</a></li>
|
||||
<li><a target="_blank" ignore-job-clear-on-click
|
||||
<li><a target="_blank" data-ignore-job-clear-on-click
|
||||
href="https://bugherder.mozilla.org/?cset={{::resultset.revision}}&tree={{::repoName}}"
|
||||
title="Use Bugherder to mark the bugs in this push">Mark with Bugherder</a></li>
|
||||
<li><a target="_blank" ignore-job-clear-on-click
|
||||
<li><a target="_blank" data-ignore-job-clear-on-click
|
||||
href="https://secure.pub.build.mozilla.org/buildapi/self-serve/{{::repoName}}/rev/{{::resultset.revision}}">BuildAPI</a></li>
|
||||
<li><a target="_blank" ignore-job-clear-on-click
|
||||
<li><a target="_blank" data-ignore-job-clear-on-click
|
||||
href=""
|
||||
ng-show="user.is_staff"
|
||||
ng-click="triggerMissingJobs(resultset.revision)">Trigger missing jobs</a></li>
|
||||
<li><a target="_blank" ignore-job-clear-on-click
|
||||
<li><a target="_blank" data-ignore-job-clear-on-click
|
||||
href=""
|
||||
ng-show="user.is_staff"
|
||||
ng-click="triggerAllTalosJobs(resultset.revision)">Trigger all Talos jobs</a></li>
|
||||
|
|
Загрузка…
Ссылка в новой задаче