REGRESSION(r212853): Comparisons to baseline no longer shows up
[WebKit-https.git] / Websites / perf.webkit.org / public / v3 / components / chart-status-view.js
1
2 class ChartStatusView extends ComponentBase {
3
4     constructor(metric, chart)
5     {
6         super('chart-status');
7         this._metric = metric;
8         this._chart = chart;
9
10         this._usedSelection = null;
11         this._usedCurrentPoint = null;
12         this._usedPreviousPoint = null;
13
14         this._currentValue = null;
15         this._comparisonClass = null;
16         this._comparisonLabel = null;
17
18         this._renderedCurrentValue = null;
19         this._renderedComparisonClass = null;
20         this._renderedComparisonLabel = null;
21     }
22
23     render()
24     {
25         this.updateStatusIfNeeded();
26
27         if (this._renderedCurrentValue == this._currentValue
28             && this._renderedComparisonClass == this._comparisonClass
29             && this._renderedComparisonLabel == this._comparisonLabel)
30             return;
31
32         this._renderedCurrentValue = this._currentValue;
33         this._renderedComparisonClass = this._comparisonClass;
34         this._renderedComparisonLabel = this._comparisonLabel;
35
36         this.content().querySelector('.chart-status-current-value').textContent = this._currentValue || '';
37         var comparison = this.content().querySelector('.chart-status-comparison');
38         comparison.className = 'chart-status-comparison ' + (this._comparisonClass || '');
39         comparison.textContent = this._comparisonLabel;
40     }
41
42     updateStatusIfNeeded()
43     {
44         var currentPoint;
45         var previousPoint;
46
47         if (this._chart instanceof InteractiveTimeSeriesChart) {
48             var selection = this._chart.currentSelection();
49             if (selection && this._usedSelection == selection)
50                 return false;
51
52             if (selection) {
53                 const view = this._chart.selectedPoints('current');
54                 if (!view)
55                     return false;
56
57                 if (view && view.length() > 1) {
58                     this._usedSelection = selection;
59                     currentPoint = view.lastPoint();
60                     previousPoint = view.firstPoint();
61                 }
62             } else  {
63                 const indicator = this._chart.currentIndicator();
64                 if (indicator) {
65                     currentPoint = indicator.point;
66                     previousPoint = indicator.view.previousPoint(currentPoint);
67                 }
68             }
69         } else {
70             var data = this._chart.sampledTimeSeriesData('current');
71             if (!data)
72                 return false;
73             if (data.length())
74                 currentPoint = data.lastPoint();
75         }
76
77         if (currentPoint == this._usedCurrentPoint && previousPoint == this._usedPreviousPoint)
78             return false;
79
80         this._usedCurrentPoint = currentPoint;
81         this._usedPreviousPoint = previousPoint;
82
83         this.computeChartStatusLabels(currentPoint, previousPoint);
84
85         return true;
86     }
87
88     computeChartStatusLabels(currentPoint, previousPoint)
89     {
90         var status = currentPoint ? this._computeChartStatus(this._metric, this._chart, currentPoint, previousPoint) : null;
91         if (status) {
92             this._currentValue = status.currentValue;
93             if (previousPoint)
94                 this._currentValue += ` (${status.valueDelta} / ${status.relativeDelta})`;
95             this._comparisonClass = status.className;
96             this._comparisonLabel = status.label;
97         } else {
98             this._currentValue = null;
99             this._comparisonClass = null;
100             this._comparisonLabel = null;
101         }
102     }
103
104     _computeChartStatus(metric, chart, currentPoint, previousPoint)
105     {
106         console.assert(currentPoint);
107         const baselineView = chart.sampledTimeSeriesData('baseline');
108         const targetView = chart.sampledTimeSeriesData('target');
109
110         const formatter = metric.makeFormatter(3);
111         const deltaFormatter = metric.makeFormatter(2, true);
112         const smallerIsBetter = metric.isSmallerBetter();
113
114         const labelForDiff = (diff, referencePoint, name, comparison) => {
115             const relativeDiff = Math.abs(diff * 100).toFixed(1);
116             const referenceValue = referencePoint ? ` (${formatter(referencePoint.value)})` : '';
117             return `${relativeDiff}% ${comparison} than ${name}${referenceValue}`;
118         };
119
120         const baselinePoint = baselineView ? baselineView.lastPointInTimeRange(0, currentPoint.time) : null;
121         const targetPoint = targetView ? targetView.lastPointInTimeRange(0, currentPoint.time) : null;
122
123         const diffFromBaseline = baselinePoint ? (currentPoint.value - baselinePoint.value) / baselinePoint.value : undefined;
124         const diffFromTarget = targetPoint ? (currentPoint.value - targetPoint.value) / targetPoint.value : undefined;
125
126         let label = null;
127         let comparison = null;
128
129         if (diffFromBaseline !== undefined && diffFromTarget !== undefined) {
130             if (diffFromBaseline > 0 == smallerIsBetter) {
131                 comparison = 'worse';
132                 label = labelForDiff(diffFromBaseline, baselinePoint, 'baseline', comparison);
133             } else if (diffFromTarget < 0 == smallerIsBetter) {
134                 comparison = 'better';
135                 label = labelForDiff(diffFromTarget, targetPoint, 'target', comparison);
136             } else
137                 label = labelForDiff(diffFromTarget, targetPoint, 'target', 'until');
138         } else if (diffFromBaseline !== undefined) {
139             comparison = diffFromBaseline > 0 == smallerIsBetter ? 'worse' : 'better';
140             label = labelForDiff(diffFromBaseline, baselinePoint, 'baseline', comparison);
141         } else if (diffFromTarget !== undefined) {
142             comparison = diffFromTarget < 0 == smallerIsBetter ? 'better' : 'worse';
143             label = labelForDiff(diffFromTarget, targetPoint, 'target', comparison);
144         }
145
146         let valueDelta = null;
147         let relativeDelta = null;
148         if (previousPoint) {
149             valueDelta = deltaFormatter(currentPoint.value - previousPoint.value);
150             relativeDelta = (currentPoint.value - previousPoint.value) / previousPoint.value;
151             relativeDelta = (relativeDelta * 100).toFixed(0) + '%';
152         }
153
154         return {className: comparison, label, currentValue: formatter(currentPoint.value), valueDelta, relativeDelta};
155     }
156
157     static htmlTemplate()
158     {
159         return `
160             <div>
161                 <span class="chart-status-current-value"></span>
162                 <span class="chart-status-comparison"></span>
163             </div>`;
164     }
165
166     static cssTemplate()
167     {
168         return `
169             .chart-status-current-value {
170                 padding-right: 0.5rem;
171             }
172
173             .chart-status-comparison.worse {
174                 color: #c33;
175             }
176
177             .chart-status-comparison.better {
178                 color: #33c;
179             }`;
180     }
181 }