blob: d896bdb3aa66b9258b8060dfad906128f0812d2f [file] [log] [blame]
<!-- The <comp-page-sk> custom element declaration.
Displays a comparison grid that allows to compare digests against each other.
Attributes:
None
Events:
None
Methods:
pageSelected - Called by the router when view is visible.
pageDeselected - Called by the router when the view is no longer visible.
-->
<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/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="bower_components/iron-icons/iron-icons.html">
<link rel="import" href="bower_components/iron-icons/notification-icons.html">
<link rel="import" href="bower_components/paper-button/paper-button.html">
<link rel="import" href="bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="bower_components/paper-listbox/paper-listbox.html">
<link rel="import" href="bower_components/paper-item/paper-item.html">
<link rel="import" href="bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="../common/imp/paramset.html">
<link rel="import" href="digest-details-sk.html">
<link rel="import" href="activity-sk.html">
<link rel="import" href="grid-sk.html">
<link rel="import" href="search-controls-sk.html">
<link rel="import" href="select-popup-sk.html">
<link rel="import" href="zoom-dialog-sk.html">
<link rel="import" href="sort-control-sk.html">
<link rel="import" href="shared-styles.html">
<dom-module id="comp-page-sk">
<template>
<style include="iron-flex iron-flex-alignment"></style>
<style include="shared-styles">
#heading {
margin: 0;
}
#heading > span {
font-weight: bold;
margin-right: 1.5em;
vertical-align: middle;
}
.headingContainer {
padding-bottom: 2em;
}
.name {
color: #7570B3;
}
.pos {
color: #1B9E77;
}
.neg {
color: #E7298A;
}
.unt {
color: #A6761D;
}
.gridContainer {
margin: 0 5em 3em 2em;
overflow-x: auto;
}
.compDiffContainer {
width: 50em;
}
.modButtonContainer {
margin-bottom: 7em;
width: 10em;
}
.diffMetricLabel {
font-weight: bold;
margin-left: 20em;
}
paper-dropdown-menu.diffMetricSelect {
width: 8em;
--paper-input-container-underline: {
display: none;
};
}
.moreColumns {
vertical-align: middle;
}
.contentWrapper {
padding: 0.5em;
}
#columnSortControl, #rowSortControl {
width: 10em;
}
#rowSortControl {
margin-top: 2em;
}
</style>
<activity-sk id="activityCompare" busy="{{_hideAll}}"></activity-sk>
<div class="vertical layout contentWrapper">
<div class="horizontal layout center headingContainer">
<div id="heading" hidden$="[[!_data.summary]]">
<span class="name">Name: [[_data.summary.name]]</span>
<span class="pos">Pos: [[_data.summary.pos]]</span>
<span class="neg">Neg: [[_data.summary.neg]]</span>
<span class="unt">Unt: [[_data.summary.untriaged]]</span>
</div>
<div>
<!-- Note:The values of the dropdown need to match the metric ids on the backend -->
<span class="diffMetricLabel">Diff Metric:</span>
<paper-dropdown-menu label="Diff metric" class="diffMetricSelect" no-label-float>
<paper-listbox id="diffMetric" class="dropdown-content" selected="{{_diffMetric}}" attr-for-selected="value">
<paper-item value="combined">Combined</paper-item>
<paper-item value="percent">Percent</paper-item>
</paper-listbox>
</paper-dropdown-menu>
</div>
</div>
<div id="mainContent" class="horizontal layout wrap">
<!-- left column -->
<div class="vertical layout leftColumn">
<div class="vertical layout modButtonContainer">
<select-popup-sk id="paramsSelect"></select-popup-sk>
</div>
<search-controls-sk id="rowSearchControls" orientation="vertical"></search-controls-sk>
<sort-control-sk id="rowSortControl" selection="{{_rowSorting}}" disabled="[[_hideAll]]"></sort-control-sk>
</div>
<!-- main column with grid -->
<div class="vertical layout gridContainer">
<div class="horizontal layout">
<search-controls-sk id="colSearchControls" orientation="horizontal"></search-controls-sk>
<sort-control-sk id="columnSortControl" selection="{{_colSorting}}" disabled="[[_hideAll]]"></sort-control-sk>
</div>
<div class="horizontal layout center">
<div class="layout vertical center">
<grid-sk id="gridCompare"></grid-sk>
<paper-button id="moreRowsButton" hidden$="[[_noMoreRows(_data)]]">
<iron-icon icon="icons:expand-more"></iron-icon>
</paper-button>
</div>
<div class="moreColumns">
<paper-button id="moreColumnsButton" hidden$="[[_noMoreColumns(_data)]]">
<iron-icon icon="icons:chevron-right"></iron-icon>
</paper-button>
</div>
</div>
</div>
<div class="vertical layout compDiffContainer">
<activity-sk id="activityCompDiff"></activity-sk>
<digest-details-sk id="digestDetails"
mode="detail"
details="[[_digestDetails.digest]]"
commits="[[_digestDetails.commits]]">
</digest-details-sk>
<digest-details-sk
id="compDetails"
mode="diff"
details="[[_diffData.left]]"
right="[[_diffData.right]]"
diff="[[_diffData.diff]]"
embedded>
</digest-details-sk>
</div>
</div>
</div>
<!-- zoom dialog -->
<zoom-dialog-sk></zoom-dialog-sk>
</template>
<script>
(function() {
// The current state of the page.
var defaultQuery = {
rowQuery: null,
columnQuery: null,
match: gold.DEFAULT_MATCH_CONFIGS,
sortRows: "",
rowsDir: "",
sortColumns: "",
columnsDir: "",
metric: ""
};
// Default values for the row and column queries.
var defaultRowQuery = sk.object.shallowCopy(gold.defaultSearchState);
defaultRowQuery.limit = 10;
var defaultColumnQuery = sk.object.shallowCopy(gold.defaultSearchState);
defaultColumnQuery.pos = true;
defaultColumnQuery.neg = true;
defaultColumnQuery.unt = false;
defaultColumnQuery.limit = 10;
// Sort options for the row and column query.
var SORT_FIELD_N_IMAGES = "count";
var SORT_FIELD_DIFF = "diff";
var rowSortOptions = [
{field: SORT_FIELD_N_IMAGES, label: "#images"},
{field: SORT_FIELD_DIFF, label: "Diff"}
];
var colSortOptions = [ {field: SORT_FIELD_DIFF, label: "Diff"} ];
var defaultRowSorting = {dir: gold.SORT_DESC, field: SORT_FIELD_DIFF};
var defaultColSorting = {dir: gold.SORT_ASC, field: SORT_FIELD_DIFF};
// ids fo the different diff metrics.
var METRIC_COMBINED = "combined";
var METRIC_PERCENT = "percent";
Polymer({
is: "comp-page-sk",
behaviors: [gold.ZoomTargetBehavior, gold.PageStateBehavior],
properties: {
_data: {
type: Object,
value: null
},
_syncParams: {
type: Boolean,
},
_diffMetric: {
type: String
}
},
ready: function() {
this.listen(this.$.paramsSelect, 'changed-selection', '_handleMatchChanged');
this.listen(this.$.gridCompare, 'diff-click', '_handleGridClick');
this.listen(this.$.gridCompare, 'diff-dblclick', '_handleGridDblClick');
this.listen(this.$.compDetails, 'zoom-clicked', "_handleZoomClicked");
this.listen(this.$.rowSearchControls, 'state-change', '_handleRowQueryChange');
this.listen(this.$.colSearchControls, 'state-change', '_handleColumnQueryChange');
this.listen(this.$.moreColumnsButton, 'click', '_handleMoreColumns');
this.listen(this.$.moreRowsButton, 'click', '_handleMoreRows');
this.listen(this.$.rowSortControl, 'sort-changed', '_load');
this.listen(this.$.columnSortControl, 'sort-changed', '_load');
this.listen(this.$.diffMetric, 'iron-select', '_load');
this.listen(this.$.digestDetails, 'triage', '_handleTriage');
this.listen(this.$.compDetails, 'triage', '_handleTriage');
this.$.rowSortControl.setItems(rowSortOptions);
this.$.columnSortControl.setItems(colSortOptions);
this._setDefaultState(gold.defaultSearchState, true);
},
pageSelected: function(ctx) {
this._query = sk.object.shallowCopy(defaultQuery);
this._query.match = gold.DEFAULT_MATCH_CONFIGS.slice()
// Use the _state variable to as the row query and reflect the URL parameters.
this._initState(ctx, this._getDefaultStateWithCorpus(defaultRowQuery));
this._query.columnQuery = this._getDefaultStateWithCorpus(defaultColumnQuery);
this._query.columnQuery.head = this._state.head;
this._query.columnQuery.include = this._state.include;
this._query.columnQuery.query = '';
this._query.columnQuery = this._addCorpus(this._query.columnQuery);
this.set("_colSorting", sk.object.shallowCopy(defaultColSorting));
this.set("_rowSorting", sk.object.shallowCopy(defaultRowSorting));
this.set("_diffMetric", METRIC_COMBINED);
this._load();
},
pageDeselected: function() {},
// _reset clears the detail views.
_reset: function() {
this.set('_diffData', {});
this.set('_digestDetails', {});
},
_load: function() {
this._reset();
sk.get("/json/paramset").then(JSON.parse).then(function (json) {
this.$.rowSearchControls.setParamSet(json);
this.$.rowSearchControls.setState(this._state);
this.$.colSearchControls.setParamSet(json);
this.$.colSearchControls.setState(this._query.columnQuery);
this.$.paramsSelect.setParamSet(json);
this.$.paramsSelect.setSelection(this._query.match);
}.bind(this)).catch(sk.errorMessage);
// Update the query object.
this._query.sortRows = this._rowSorting.field;
this._query.rowsDir = this._rowSorting.dir;
this._query.sortColumns = this._colSorting.field,
this._query.columnsDir = this._colSorting.dir;
this._query.metric = this._diffMetric;
this._query.rowQuery = sk.object.shallowCopy(this._state);
var activity = this.$.activityCompare;
activity.startSpinner("Loading...");
this.set("_data", null);
sk.post("/json/cmp", JSON.stringify(this._query)).then(JSON.parse).then(function (json) {
activity.stopSpinner();
// If there is only one test set the summary. Otherwise inject it
// into the individual rows.
if (Object.keys(json.summaries).length === 1) {
json.summary = json.summaries[json.grid.rows[0].test];
json.summary.name = json.grid.rows[0].test;
} else {
// Inject the summaries into every test.
for(var i=0; i < json.grid.rows.length; i++) {
json.grid.rows[i].summary = json.summaries[json.grid.rows[i].test];
}
}
this.set("_data", json);
this.$.gridCompare.setValue(json.grid);
}.bind(this)).catch(function(e) {
activity.stopSpinner();
sk.errorMessage(e);
});
},
_handleGridClick: function(ev) {
this.$.compDetails.clear();
this.$.digestDetails.clear();
if (ev.detail.colDigest) {
var url = "/json/diff" + gold.diffQuery(ev.detail.test, ev.detail.rowDigest, ev.detail.colDigest);
this.$.gridCompare.clearSelection();
gold.loadWithActivity(this, url, this.$.activityCompDiff, function(json) {
this.set('_diffData', json);
this.$.gridCompare.selectCell(ev.detail.rowIndex, ev.detail.columnIndex);
}.bind(this));
} else {
var url = "/json/details" + gold.detailQuery(ev.detail.test, ev.detail.rowDigest);
this.$.gridCompare.clearSelection();
gold.loadWithActivity(this, url, this.$.activityCompDiff, function(json) {
this.set('_digestDetails', json);
this.$.gridCompare.selectRow(ev.detail.rowIndex);
}.bind(this));
}
},
_handleGridDblClick: function(ev) {
if (ev.detail.colDigest) {
var zoomDetail = {
leftImgUrl: gold.imgHref(ev.detail.rowDigest),
rightImgUrl: gold.imgHref(ev.detail.colDigest),
middleImgUrl: gold.diffImgHref(ev.detail.rowDigest, ev.detail.colDigest),
llabel: "Row",
rlabel: "Column"
};
$$$('zoom-dialog-sk', this).open(zoomDetail);
}
},
_handleTriage: function(ev) {
ev.stopPropagation();
sk.post('/json/triage', JSON.stringify(ev.detail)).catch(sk.errorMessage);
},
_handleMatchChanged: function(ev) {
this._query.match = ev.detail;
this._load();
},
_handleRowQueryChange: function(ev) {
// Reload the page with the new state in the query parameters.
this._redirectToState(ev.detail);
},
_handleColumnQueryChange: function(ev) {
this._query.columnQuery = this._addCorpus(ev.detail);
this._load();
},
// copy the query string from the row query to the column query.
_copyQueryStr: function() {
var newState = sk.object.shallowCopy(this.$.colSearchControls.state);
newState.query = this._state.query;
this.$.colSearchControls.setState(this._state, true);
},
_handleMoreRows: function() {
this._state.limit += 10;
this._load();
},
_handleMoreColumns: function() {
if (this._data) {
this._query.columnQuery.limit += 10;
this._load();
}
},
_noMoreRows: function(data) {
return data && (data.grid.rows.length >= data.grid.rowTotal);
},
_noMoreColumns: function(data) {
if (data) {
for(var i=0; i < data.grid.rows.length; i++) {
if (data.grid.rows[i].values.length >= data.grid.columnsTotal) {
return true;
}
}
return false;
}
return true;
}
});
})();
</script>
</dom-module>