Skip to content

Commit d337ba9

Browse files
Bugfixes and minor improvements (#858)
* fix incomplete techs bug * sort filterts alphabetically * keep ALL as a technology option * show all technologies when choosing all categories * revert sort order and add fallback for selected techs * scroll to the report content when clicking update (#851) * show formatted page weight * format tooltip with correct timezone * format pageweight in summary view * change feedback url and style beta label differently * linting * fix inverted color bug * replace dev with prod API * remove placeholder images * Update src/js/techreport/utils/data.js Co-authored-by: Rick Viscomi <rviscomi@users.noreply.github.com> * remove secondary value * remove bytes from weight timeseries and move formatting to reusable function * innerhtml to textcontent --------- Co-authored-by: Rick Viscomi <rviscomi@users.noreply.github.com>
1 parent 9edc219 commit d337ba9

11 files changed

Lines changed: 199 additions & 58 deletions

File tree

config/techreport.json

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,10 @@
115115
"url": "#lighthouse&median-lighthouse-over-time=accessibility"
116116
},
117117
{
118-
"endpoint": "lighthouse",
119-
"category": "seo",
120-
"metric": "median_score_pct",
118+
"endpoint": "pageWeight",
119+
"category": "total",
120+
"metric": "median_bytes_formatted",
121121
"label": "Page weight",
122-
"suffix": "kB",
123122
"description": "Median total bytes per page for across tested origins.",
124123
"url": "#page-weight"
125124
}
@@ -525,26 +524,23 @@
525524
{
526525
"endpoint": "pageWeight",
527526
"category": "images",
528-
"metric": "median_bytes",
527+
"metric": "median_bytes_formatted",
529528
"label": "Image Weight",
530-
"url": "?weight-over-time=image#page-weight",
531-
"suffix": " kB"
529+
"url": "?weight-over-time=image#page-weight"
532530
},
533531
{
534532
"endpoint": "pageWeight",
535533
"category": "js",
536-
"metric": "median_bytes",
534+
"metric": "median_bytes_formatted",
537535
"label": "JavaScript Size",
538-
"url": "?weight-over-time=js#page-weight",
539-
"suffix": " kB"
536+
"url": "?weight-over-time=js#page-weight"
540537
},
541538
{
542539
"endpoint": "pageWeight",
543540
"category": "total",
544-
"metric": "median_bytes",
541+
"metric": "median_bytes_formatted",
545542
"label": "Total Page Weight",
546-
"url": "?weight-over-time=total#page-weight",
547-
"suffix": " kB"
543+
"url": "?weight-over-time=total#page-weight"
548544
}
549545
]
550546
},
@@ -586,8 +582,7 @@
586582
},
587583
{
588584
"name": "Median Bytes",
589-
"key": "median_bytes",
590-
"suffix": "kB",
585+
"key": "median_bytes_formatted",
591586
"className": "main-cell"
592587
},
593588
{
@@ -603,19 +598,21 @@
603598
"default": "images",
604599
"title": "Image weight over time",
605600
"metric": "median_bytes",
601+
"suffix": "bytes",
602+
"metric_summary": "median_bytes_formatted",
606603
"height": 600,
607604
"series": {
608605
"breakdown": "client",
609606
"values": [
610607
{
611608
"name": "desktop",
612609
"color": "#669E8E",
613-
"suffix": " kB"
610+
"suffix": " bytes"
614611
},
615612
{
616613
"name": "mobile",
617614
"color": "#BD6EBE",
618-
"suffix": " kB"
615+
"suffix": " bytes"
619616
}
620617
],
621618
"defaults": [
@@ -648,7 +645,7 @@
648645
]
649646
},
650647
"yAxis": {
651-
"title": "Weight in kB"
648+
"title": "Weight in bytes"
652649
}
653650
}
654651
}
@@ -978,8 +975,7 @@
978975
},
979976
{
980977
"name": "Median Bytes",
981-
"key": "median_bytes",
982-
"suffix": "kB",
978+
"key": "median_bytes_formatted",
983979
"className": "main-cell",
984980
"breakdown": "app"
985981
},
@@ -997,17 +993,20 @@
997993
"param": "median-weight-over-time",
998994
"default": "total",
999995
"metric": "median_bytes",
996+
"metric_summary": "median_bytes_formatted",
1000997
"series": {
1001998
"breakdown": "app",
1002-
"suffix": " kB",
999+
"suffix": " bytes",
10031000
"defaults": [
10041001
{
10051002
"name": "App",
1006-
"data": [0,0,0,0,0,0,0,0,0,0,0,0]
1003+
"data": [0,0,0,0,0,0,0,0,0,0,0,0],
1004+
"suffix": " bytes"
10071005
},
10081006
{
10091007
"name": "App",
1010-
"data": [0,0,0,0,0,0,0,0,0,0,0,0]
1008+
"data": [0,0,0,0,0,0,0,0,0,0,0,0],
1009+
"suffix": " bytes"
10111010
}
10121011
]
10131012
},
@@ -1030,7 +1029,7 @@
10301029
]
10311030
},
10321031
"yAxis": {
1033-
"title": "Weight in kB"
1032+
"title": "Weight in bytes"
10341033
}
10351034
}
10361035
},

server/csp.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"lux.speedcurve.com",
1313
"'unsafe-inline'",
1414
"dev-gw-2vzgiib6.ue.gateway.dev",
15+
"prod-gw-2vzgiib6.ue.gateway.dev",
1516
],
1617
"font-src": ["'self'"],
1718
"connect-src": [
@@ -26,6 +27,7 @@
2627
"*.analytics.google.com",
2728
"stats.g.doubleclick.net",
2829
"dev-gw-2vzgiib6.ue.gateway.dev",
30+
"prod-gw-2vzgiib6.ue.gateway.dev",
2931
],
3032
"img-src": ["'self'", "https:"],
3133
"frame-src": ["'none'"],

src/js/components/filters.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class Filters {
6363
url.searchParams.delete('rank');
6464
url.searchParams.append('rank', rank);
6565

66+
/* Scroll to the report content */
67+
url.hash = '#report-content';
6668

6769
/* Update the url */
6870
location.href = url;

src/js/techreport/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ class TechReport {
174174
},
175175
];
176176

177-
const base = 'https://dev-gw-2vzgiib6.ue.gateway.dev/v1';
177+
const base = 'https://prod-gw-2vzgiib6.ue.gateway.dev/v1';
178178

179179
const technology = technologies.join('%2C')
180180
.replaceAll(" ", "%20");
@@ -239,7 +239,7 @@ class TechReport {
239239
// Fetch the data for the filter dropdowns
240240
getFilterInfo() {
241241
const filterData = {};
242-
const base = 'https://dev-gw-2vzgiib6.ue.gateway.dev/v1';
242+
const base = 'https://prod-gw-2vzgiib6.ue.gateway.dev/v1';
243243

244244
const filterApis = ['categories', 'technologies', 'ranks', 'geos'];
245245

src/js/techreport/timeseries.js

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ class Timeseries {
4949
const button = event.target;
5050
const tableWrapper = document.getElementById(`${button.dataset.id}-table-wrapper`);
5151
if(tableWrapper.classList.contains('hidden')) {
52-
button.innerHTML = 'Hide table';
52+
button.textContent = 'Hide table';
5353
tableWrapper.classList.remove('hidden');
5454
} else {
55-
button.innerHTML = 'Show table';
55+
button.textContent = 'Show table';
5656
tableWrapper.classList.add('hidden');
5757
}
5858
}
@@ -108,6 +108,7 @@ class Timeseries {
108108
/* Get settings */
109109
const metric = viz.dataset.metric;
110110
const endpoint = viz.dataset.endpoint;
111+
const summary = viz.dataset.summary;
111112

112113
const app = pageFilters.app[0];
113114
const filtered = data?.[app]?.filter(entry => entry[endpoint]);
@@ -143,11 +144,19 @@ class Timeseries {
143144
breakdownLabel.classList.add('breakdown-label');
144145
itemWrapper.appendChild(breakdownLabel);
145146

146-
/* Add the value to the wrapper */
147-
const valueLabel = document.createElement('p');
148-
valueLabel.textContent = `${latestValue}${breakdown.suffix || ''}`;
149-
valueLabel.classList.add('breakdown-value');
150-
itemWrapper.appendChild(valueLabel);
147+
/* If defined, use a different metric for the summary */
148+
if(summary) {
149+
const valueLabel = document.createElement('p');
150+
valueLabel.textContent = categoryData?.[breakdown.name]?.[summary];
151+
valueLabel.classList.add('breakdown-value');
152+
itemWrapper.appendChild(valueLabel);
153+
} else {
154+
/* Add the value to the wrapper */
155+
const valueLabel = document.createElement('p');
156+
valueLabel.textContent = `${latestValue}${breakdown.suffix || ''}`;
157+
valueLabel.classList.add('breakdown-value');
158+
itemWrapper.appendChild(valueLabel);
159+
}
151160

152161
/* Add the wrapper to the container */
153162
container.appendChild(itemWrapper);
@@ -173,6 +182,7 @@ class Timeseries {
173182
const metric = component.dataset.metric;
174183
const endpoint = component.dataset.endpoint;
175184
const client = component.dataset.client;
185+
const summary = component.dataset.summary;
176186

177187
pageFilters.app.forEach((app, index) => {
178188
if(data[app] && data[app].length > 0) {
@@ -184,6 +194,7 @@ class Timeseries {
184194
const latestSubcategory = latestEndpoint?.find(row => row.name === subcategory);
185195
const latestClient = latestSubcategory?.[client];
186196
const latestValue = latestClient?.[metric];
197+
const summaryValue = latestClient?.[summary];
187198

188199
/* Select the container to which we'll add elements. */
189200
const card = container.querySelector(`[data-app="${app}"]`);
@@ -192,14 +203,18 @@ class Timeseries {
192203
const value = card.getElementsByClassName('breakdown-value')[0];
193204

194205
/* Update text */
195-
label.innerHTML = latest.technology;
206+
label.textContent = latest.technology;
196207
if(latestValue) {
197-
value.innerHTML = `${latestValue}${config.series.suffix || ''}`;
208+
if(summary) {
209+
value.textContent = `${summaryValue}`;
210+
} else {
211+
value.textContent = `${latestValue}${config.series.suffix || ''}`;
212+
}
198213
} else {
199214
value.classList.add('undefined');
200-
value.innerHTML = 'No data';
215+
value.textContent = 'No data';
201216
}
202-
timestamp.innerHTML = latest.date;
217+
timestamp.textContent = latest.date;
203218
const techColor = UIUtils.getAppColor(app, this.pageFilters.app, this.pageConfig.colors);
204219
const fallback = this.pageConfig.colors.app[index];
205220
card.style.setProperty('--breakdown-color', techColor || fallback);
@@ -248,6 +263,97 @@ class Timeseries {
248263
timeseries.series = this.formatSeries();
249264
}
250265

266+
timeseries.tooltip = {
267+
shared: true,
268+
crosshairs: true,
269+
useHTML: true,
270+
formatter: function() {
271+
const wrapper = document.createElement('div');
272+
wrapper.className = 'tooltip-wrapper';
273+
274+
const d = Highcharts.dateFormat('%b %e, %Y', this.x);
275+
276+
const dateEl = document.createElement('p');
277+
dateEl.innerHTML = d;
278+
279+
wrapper.appendChild(dateEl);
280+
281+
const pointList = document.createElement('ul');
282+
283+
this.points.forEach(point => {
284+
const pointItem = document.createElement('li');
285+
const pointSeries = document.createElement('span');
286+
287+
const pointSvg = document.createElement('svg');
288+
let pointSymbol;
289+
290+
291+
switch(point?.point?.graphic?.symbolName) {
292+
case 'circle':
293+
pointSymbol = document.createElement('circle');
294+
pointSymbol.setAttribute('class', 'point-symbol circle');
295+
pointSymbol.setAttribute('r', point.point.graphic.width / 2);
296+
pointSymbol.setAttribute('stroke', point.color);
297+
pointSymbol.setAttribute('stroke-width', point.point.graphic['stroke-width']);
298+
break;
299+
300+
case 'diamond':
301+
pointSymbol = document.createElement('path');
302+
pointSymbol.setAttribute('class', 'point-symbol diamond');
303+
pointSymbol.setAttribute('d', 'M 4 0 L 8 4 L 4 8 L 0 4 Z');
304+
pointSymbol.setAttribute('stroke', point.color);
305+
pointSymbol.setAttribute('stroke-width', point.point.graphic['stroke-width']);
306+
break;
307+
308+
case 'square':
309+
pointSymbol = document.createElement('path');
310+
pointSymbol.setAttribute('class', 'point-symbol square');
311+
pointSymbol.setAttribute('d', 'M 0 0 L 8 0 L 8 8 L 0 8 Z');
312+
pointSymbol.setAttribute('stroke', point.color);
313+
pointSymbol.setAttribute('stroke-width', point.point.graphic['stroke-width']);
314+
break;
315+
316+
case 'triangle-down':
317+
pointSymbol = document.createElement('path');
318+
pointSymbol.setAttribute('class', 'point-symbol triangle-down');
319+
pointSymbol.setAttribute('d', 'M 0 0 L 8 0 L 4 8 Z');
320+
pointSymbol.setAttribute('stroke', point.color);
321+
pointSymbol.setAttribute('stroke-width', point.point.graphic['stroke-width']);
322+
break;
323+
324+
case 'triangle':
325+
pointSymbol = document.createElement('path');
326+
pointSymbol.setAttribute('class', 'point-symbol triangle-up');
327+
pointSymbol.setAttribute('d', 'M 4 0 L 8 8 L 0 8 Z');
328+
pointSymbol.setAttribute('stroke', point.color);
329+
pointSymbol.setAttribute('stroke-width', point.point.graphic['stroke-width']);
330+
break;
331+
332+
333+
default:
334+
pointSymbol = document.createElement('circle');
335+
pointSymbol.setAttribute('class', 'point-fallback');
336+
pointSymbol.setAttribute('r', '4');
337+
pointSymbol.setAttribute('fill', point.color);
338+
break;
339+
}
340+
341+
pointSvg.appendChild(pointSymbol);
342+
343+
document.getElementsByTagName('main')[0].append(pointSvg);
344+
345+
pointSeries.innerHTML = point.series.name;
346+
pointItem.innerHTML = `${pointSvg.outerHTML} ${pointSeries.outerHTML}: ${point.y}`;
347+
348+
pointList.appendChild(pointItem);
349+
});
350+
351+
wrapper.appendChild(pointList);
352+
353+
return wrapper.outerHTML;
354+
}
355+
}
356+
251357
// Render the chart
252358
Highcharts.chart(`${this.id}-timeseries`, timeseries);
253359
}
@@ -298,7 +404,7 @@ class Timeseries {
298404
const data = app.map(row => {
299405
const value = row?.[endpoint]?.find(row => row.name === subcategory)?.[client]?.[metric];
300406
return {
301-
x: new Date(row.date),
407+
x: new Date(row.date).getTime(),
302408
y: value || 0,
303409
};
304410
});
@@ -341,7 +447,7 @@ class Timeseries {
341447
const clientData = categoryData?.[value.name];
342448
const y = clientData?.[metric];
343449
formattedData.push({
344-
x: new Date(row.date),
450+
x: new Date(row.date).getTime(),
345451
y: Number(y),
346452
});
347453
});

0 commit comments

Comments
 (0)