blob: d3fc389dc1ea71545fb45cb28f98c873a701d1d2 [file] [log] [blame]
<!--
The <newsearch-page-sk> custom element declaration.
Shows the results of a search request.
It sends the query string as a JSON request to the
search entpoint ('/json/search') and renders the result.
It assumes to the be part of a client site routed system
of views and therefore offers the 'pageSelected' and 'pageUnselected'
functions. These need to be called whenever the page goes
in and out of view.
Attributes:
None
Methods:
pageSelected(ctx) - Called by the router when the view becomes visible.
ctx is the context provided in the route dispatch of page.js.
pageDeselected - Called by the router when the view is no longer visible.
Events:
None
Mailboxes:
None
-->
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout-classes.html">
<link rel="import" href="bower_components/iron-icons/iron-icons.html">
<link rel="import" href="bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="bower_components/paper-button/paper-button.html">
<link rel="import" href="bower_components/paper-menu-button/paper-menu-button.html">
<link rel="import" href="activity-sk.html">
<link rel=import href="detail-list-sk.html">
<link rel=import href="newdigest-details-sk.html">
<link rel=import href="search-controls-sk.html">
<link rel=import href="trybot-controls-sk.html">
<link rel="import" href="../common/imp/triage-sk.html">
<dom-module id="newsearch-page-sk">
<template>
<style include="iron-flex iron-flex-alignment"></style>
<style include="shared-styles">
newdigest-details-sk {
display: block;
box-shadow: 3px 3px 6px 1px rgba(133,133,133,1);
margin-top: 1em;
margin-bottom: 1em;
margin-left: 0;
margin-right: 0;
padding-left: 1em;
padding-top: 1em;
padding-bottom: 1em;
padding-right: 0;
}
newdigest-details-sk[data-focus] {
box-shadow: 3px 3px 6px 5px #FF7F00;
}
#missing {
padding-top: 2em;
}
.searchPageWrapper {
padding-left: 1em;
padding-top: 1em;
margin-left:1em;
margin-right: 3em;
}
#triggerSearchButton {
font-size: 18px;
}
.helpIconContainer {
padding-left: 3em;
}
paper-button {
min-width: 2em;
margin: 5px;
background: #eee;
}
.actionMenu {
max-width: 10em;
margin-left: 10em;
}
.actionButton {
color: green;
};
#closestButton[raised] > iron-icon {
color: #bbb;
}
#closestButton[raised] {
background: #fff;
}
#closestButton:hover,
#closestButton[raised]:hover {
background: #ddd;
}
.searchResponse {
padding: 1em;
margin: 1em 1em 1em 0;
border: 1px solid #555555;
}
</style>
<div class="searchPageWrapper layout vertical">
<activity-sk id="activityBar" busy="{{_hideAll}}"></activity-sk>
<div class="layout horizontal">
<search-controls-sk id="searchControls" beta></search-controls-sk>
<div>
<paper-button id="triggerSearchButton" raised disabled="[[_hideAll]]">Search</paper-button>
</div>
<paper-menu-button close-on-activate no-animations class="actionMenu">
<paper-button class="dropdown-trigger actionButton" raised>Actions</paper-button>
<paper-menu class="dropdown-content">
<paper-item id="actionTestView">Switch To Test View</paper-item>
<paper-item id="actionBulkTriage">Bulk Triage</paper-item>
<paper-item id="actionLegacySearch">Legacy Search</paper-item>
<paper-item id="actionHelpDialog">Help</paper-item>
</paper-menu>
</paper-menu-button>
</div>
<div class="searchResponse" hidden$="[[_noResult(_allData)]]">
Displaying: [[_allData.size]] / [[_allData.digests.length]] @ [[_allData.offset]]<br>
UniqueTests: [[_uniqueTests.length]] <br>
UniqueTests All: [[_uniqueTestsAll.length]]<br>
</div>
<trybot-controls-sk id="trybotControls"></trybot-controls-sk>
<div hidden$="[[_hideAll]]">
<div id="missing" hidden$="[[_nonEmptyResult(data)]]">
No digests match your query.
</div>
<div hidden$="[[_emptyResult(data)]]">
<detail-list-sk id="detailList">
<template is="dom-repeat" items="[[data.digests]]">
<newdigest-details-sk
id$="[[_entryId(item)]]"
mode="list"
triage
details="[[item]]"
commits="[[data.commits]]"
metric="[[_state.metric]]">
</newdigest-details-sk>
</template>
</detail-list-sk>
</div>
</div>
</div>
<paper-dialog id="bulkDialog" with-backdrop>
<h2>Bulk Triage</h2>
<p>Assign the status to all images on this page at once.</p>
<div class="layout horizontal">
<triage-sk value="{{_bulkStatus}}" id="triageControls"></triage-sk>
<paper-button id="closestButton" on-tap="_handleClosestButton" title="Assign status of closest image">
<iron-icon icon="icons:view-agenda"></iron-icon>
</paper-button>
</div>
<div>
</div>
<div class="buttons">
&quot;{{_bulkStatus}}&quot;
<paper-button raise dialog-dismiss>Cancel</paper-button>
<paper-button raise dialog-dismiss on-tap="_doBulkTriage" disabled="[[!_bulkStatus]]">Triage</paper-button>
</div>
</paper-dialog>
</template>
<script>
Polymer({
is: "newsearch-page-sk",
behaviors: [gold.PageStateBehavior],
properties: {
data: {
type: Object,
value: function() { return {}; }
},
_bulkStatus: {
type: String,
value: "",
observer: "_handleBulkStatusChanged"
}
},
ready: function() {
this.listen(this.$.searchControls, 'state-change', '_handleStateChange');
this.listen(this.$.triggerSearchButton, 'tap', '_handleSearchButton');
this.listen(this.$.actionTestView, 'tap', '_handleSwitchToByTest');
this.listen(this.$.actionBulkTriage, 'tap', '_handleBulkButton');
this.listen(this.$.actionLegacySearch, 'tap', '_handleLegacySearchButton');
this.listen(this.$.actionHelpDialog, 'tap', '_openHelpDialog');
this._setDefaultState(gold.defaultSearchState, false);
},
pageSelected: function(ctx) {
this.set('data', {});
this.set('_allData', {});
this.$.detailList.startUse();
// Initialize the state and set values of the controls.
this._initState(ctx, this._getDefaultStateWithCorpus());
this.$.searchControls.setState(this._state);
// _loadParamset will also trigger loading the data.
this._loadParamset();
},
pageDeselected: function() {
this.$.detailList.endUse();
},
_handleSearchButton: function(ev) {
// Reload the page with the current state of the controls.
this._redirectToState(this.$.searchControls.state);
},
_handleStateChange: function(ev) {
// Reload the page with the new state in the query parameters.
this._redirectToState(ev.detail);
},
_handleSwitchToByTest: function() {
this._redirectToState({}, "/list");
},
_openHelpDialog: function(ev) {
this.$.detailList.openHelpDialog();
},
_handleBulkButton: function(ev) {
this.set("_bulkStatus", "");
this.$.bulkDialog.open();
},
_handleLegacySearchButton: function(ev) {
this._redirectToState({}, "/search");
},
_handleClosestButton: function(ev) {
this.set("_bulkStatus", '=');
},
_handleBulkStatusChanged: function(newVal) {
if (newVal === '=') {
Polymer.dom(this.$.closestButton).removeAttribute('raised');
} else {
Polymer.dom(this.$.closestButton).setAttribute('raised', '');
}
},
_doBulkTriage: function(ev) {
var targetStatus = this.$.triageControls.value;
var digests = this.data.digests;
var triageList = [];
if (targetStatus === '=') {
for(var i=0; i < digests.length; i++) {
var element= this.$$('#' + this._entryId(digests[i]));
if (element) {
var status = element.statusClosest();
if (status) {
triageList.push([digests[i].test, digests[i].digest, status]);
}
}
}
} else {
for(var i=0; i < digests.length; i++) {
triageList.push([digests[i].test, digests[i].digest, targetStatus]);
}
}
var query = gold.makeTriageQuery(triageList);
this.$.activityBar.startSpinner("Triaging ...");
sk.post('/json/triage', JSON.stringify(query)).then(function() {
this.$.activityBar.stopSpinner();
}.bind(this)).catch(function(e) {
this.$.activityBar.stopSpinner();
sk.errorMessage(e);
}.bind(this));
},
_load: function() {
var q = window.location.search;
sk.get("/json/newsearch" + q).then(JSON.parse).then(function (json) {
// Split out the digests that are being displayed and keep a copy
// of all digests that matched the query.
var displayData = sk.object.shallowCopy(json);
displayData.digests = displayData.digests.slice(json.offset, json.offset+json.size);
this.set('_allData', json);
this.set('_uniqueTestsAll', this._getUniqueTests(json.digests));
this.set('_uniqueTests', this._getUniqueTests(displayData.digests));
this.set('data', displayData);
this.$.trybotControls.setIssue(json.issue);
this.$.activityBar.stopSpinner();
}.bind(this)).catch(function(e) {
this.$.activityBar.stopSpinner();
sk.errorMessage(e);
}.bind(this));
},
_getUniqueTests: function(digests) {
var unique = {};
for(var i=0; i < digests.length; i++) {
unique[digests[i].test] = true;
}
return Object.keys(unique);
},
_loadParamset: function() {
this.$.activityBar.startSpinner("Loading ...");
// If we already have a paramset then trigger load right away.
// TODO(stephana): Improve this so that we load the paramset with the
// data.
var loadTriggered = false;
if (this._paramSet) {
this._load();
loadTriggered = true;
}
sk.get("/json/paramset").then(JSON.parse).then(function (json) {
this.$.searchControls.setParamSet(json);
this._paramSet = json;
if (!loadTriggered) {
this._load();
}
}.bind(this)).catch(sk.errorMessage);
},
_entryId: function(item) {
// Make sure it's a valid selector.
return (item.test + '_' + item.digest).replace(/[^_A-Za-z0-9]/g, '_');
},
// Returns true if there was a query and result is not empty.
_nonEmptyResult: function(data) {
return !data.digests || data.digests.length > 0;
},
// Returns true if there was a query and the result is empty.
_emptyResult: function(data) {
return !data.digests || data.digests.length === 0;
},
_noResult: function(data) {
return !data.digests;
}
});
</script>
</dom-module>