Skip to content

Commit

Permalink
feat(rum-explorer): display conversion rate per facet entry
Browse files Browse the repository at this point in the history
  • Loading branch information
akalfas committed Jun 19, 2024
1 parent 0e19c52 commit 20797be
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 27 deletions.
16 changes: 14 additions & 2 deletions tools/rum/elements/list-facet.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { scoreCWV, toHumanReadable, escapeHTML } from '../utils.js';
import {
computeConversionRate, escapeHTML, scoreCWV, toHumanReadable,
} from '../utils.js';
import { pValue } from '../cruncher.js';

async function addSignificanceFlag(element, metric, baseline) {
Expand Down Expand Up @@ -210,7 +212,17 @@ export default class ListFacet extends HTMLElement {
countspan.textContent = toHumanReadable(entry.metrics.pageViews.sum);
countspan.title = entry.metrics.pageViews.sum;
const valuespan = this.createValueSpan(entry);
label.append(valuespan, countspan);

const conversionspan = document.createElement('span');
conversionspan.className = 'extra';

const conversions = entry.metrics.conversions.sum;
const visits = entry.metrics.visits.sum;
const conversionRate = computeConversionRate(conversions, visits);
conversionspan.textContent = toHumanReadable(conversionRate);
conversionspan.title = conversionRate;

label.append(valuespan, countspan, conversionspan);

const ul = document.createElement('ul');
ul.classList.add('cwv');
Expand Down
47 changes: 31 additions & 16 deletions tools/rum/rum-slicer.css
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ main .key-metrics ul > #visits p span.extra::after {

conversion-tracker {
display: grid;
grid-template-areas:
'heading heading button'
grid-template-areas:
'heading heading button'
'number empty button'
'number definition definition'
;
Expand Down Expand Up @@ -383,8 +383,8 @@ figcaption > span {
background-color: var(--light-purple);
}

.filter-tags > span.filter-tag-lcp,
.filter-tags > span.filter-tag-cls,
.filter-tags > span.filter-tag-lcp,
.filter-tags > span.filter-tag-cls,
.filter-tags > span.filter-tag-inp,
.filter-tags > span.filter-tag-conversions,
.filter-tags > span.filter-tag-visits {
Expand Down Expand Up @@ -568,15 +568,17 @@ vitals-facet label::before {
word-break: break-all;
}

#facets fieldset label span.count {
#facets fieldset label span.count, #facets fieldset label span.extra {
opacity: 0.6;
}

#facets fieldset div:hover span.count {
#facets fieldset div:hover span.count, #facets fieldset div:hover span.extra {
opacity: 1;
}

#facets fieldset label span.count::before, #facets fieldset label span.value::before {
#facets fieldset label span.count::before,
#facets fieldset label span.value::before,
#facets fieldset label span.extra::before {
content: ' (';
}

Expand All @@ -588,6 +590,19 @@ vitals-facet label::before {
content: ')';
}

#facets fieldset label span.extra::after {
content: '%)';
}

#facets fieldset span.interesting {
background: orange;
}

#facets fieldset span.significant {
background: red;
}


#facets literal-facet label span.value::after {
content: '';
}
Expand Down Expand Up @@ -615,7 +630,7 @@ thumbnail-facet fieldset div:not(.more-container) {

vitals-facet fieldset {
display: grid;
grid-template-areas:
grid-template-areas:
'. lcp-good cls-good inp-good'
'. lcp-ni cls-ni inp-ni'
'. lcp-poor cls-poor inp-poor';
Expand Down Expand Up @@ -759,7 +774,7 @@ vitals-facet fieldset label {
}

#facets fieldset div.load-more {
grid-column: 2 / span 2;
grid-column: 2 / span 2;
display: block;
padding: 10px 0;
}
Expand Down Expand Up @@ -994,18 +1009,18 @@ facet-sidebar[aria-disabled="true"] div.quick-filter {
main .key-metrics ul > * h2, main .key-metrics ul > * button {
font-size: 14px;
}

main .key-metrics ul > * h2::before {
width: 14px;
height: 14px;
border-radius: 14px;
}

main .key-metrics ul > * p {
font-size: 30px;
margin: 16px 0;
}

main .key-metrics ul {
grid-gap: 16px;
}
Expand All @@ -1031,7 +1046,7 @@ facet-sidebar[aria-disabled="true"] div.quick-filter {
border-radius: 16px;
padding: 4px 16px;
font-size: 18px;
}
}

#facets link-facet a .protocol {
display: inline;
Expand All @@ -1054,7 +1069,7 @@ facet-sidebar[aria-disabled="true"] div.quick-filter {

main .key-metrics ul {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}
}

#deepmain > div, #deepmain > facet-sidebar {
width: 50%;
Expand Down Expand Up @@ -1095,7 +1110,7 @@ facet-sidebar[aria-disabled="true"] div.quick-filter {
@media (min-width: 2000px) {
main .key-metrics ul {
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}
}

#facets link-facet a .protocol {
display: inline;
Expand All @@ -1108,4 +1123,4 @@ facet-sidebar[aria-disabled="true"] div.quick-filter {

footer.footer-wrapper.appear {
display: none;
}
}
20 changes: 12 additions & 8 deletions tools/rum/slicer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
import { DataChunks } from './cruncher.js';
import DataLoader from './loader.js';
import {
parseConversionSpec, parseSearchParams, isKnownFacet, scoreCWV, toHumanReadable,
parseConversionSpec,
parseSearchParams,
isKnownFacet,
scoreCWV,
toHumanReadable,
computeConversionRate,
} from './utils.js';

/* globals */
Expand Down Expand Up @@ -36,7 +41,9 @@ dataChunks.addSeries('lcp', (bundle) => bundle.cwvLCP);
dataChunks.addSeries('cls', (bundle) => bundle.cwvCLS);
dataChunks.addSeries('inp', (bundle) => bundle.cwvINP);
dataChunks.addSeries('ttfb', (bundle) => bundle.cwvTTFB);

dataChunks.addSeries('conversions', (bundle) => (dataChunks.hasConversion(bundle, parseConversionSpec())
? bundle.weight
: 0));
function setDomain(domain, key) {
DOMAIN = domain;
loader.domain = domain;
Expand All @@ -59,8 +66,8 @@ export function updateKeyMetrics(keyMetrics) {

document.querySelector('#conversions p').textContent = toHumanReadable(keyMetrics.conversions);
const conversionsExtra = document.createElement('span');
conversionsExtra.textContent = toHumanReadable((
100 * keyMetrics.conversions) / keyMetrics.pageViews);
const conversionRate = computeConversionRate(keyMetrics.conversions, keyMetrics.visits);
conversionsExtra.textContent = toHumanReadable(conversionRate);
conversionsExtra.className = 'extra';
document.querySelector('#conversions p').appendChild(conversionsExtra);

Expand Down Expand Up @@ -222,16 +229,13 @@ export async function draw() {

await herochart.draw();

const facets = dataChunks.facets.conversions;
const converted = facets?.find((f) => f.value === 'converted');

updateKeyMetrics({
pageViews: dataChunks.totals.pageViews.sum,
lcp: dataChunks.totals.lcp.percentile(75),
cls: dataChunks.totals.cls.percentile(75),
inp: dataChunks.totals.inp.percentile(75),
ttfb: dataChunks.totals.ttfb.percentile(75),
conversions: converted ? converted.weight : 0,
conversions: dataChunks.totals.conversions.sum,
visits: dataChunks.totals.visits.sum,
bounces: dataChunks.totals.bounces.sum,
});
Expand Down
29 changes: 28 additions & 1 deletion tools/rum/test/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { truncate, escapeHTML } from '../utils.js';
import { truncate, escapeHTML, computeConversionRate } from '../utils.js';

describe('truncate', () => {
it('truncates to the beginning of the hour', () => {
Expand Down Expand Up @@ -46,3 +46,30 @@ describe('escapeHTML', () => {
assert.strictEqual(escapeHTML('<div>hello</div>'), '&#60;div&#62;hello&#60;/div&#62;');
});
});

describe('computeConversionRate', () => {
it('its 10% for 1 conversion and 10 visits', () => {
const result = computeConversionRate(1, 10);
assert.strictEqual(result, 10);
});
it('its 100% for 10 conversion and 10 visits', () => {
const result = computeConversionRate(10, 10);
assert.strictEqual(result, 100);
});
it('its 0% for 0 conversion and 10 visits', () => {
const result = computeConversionRate(0, 10);
assert.strictEqual(result, 0);
});
it('its 100% for 1 conversion and 0 visits', () => {
const result = computeConversionRate(1, 0);
assert.strictEqual(result, 100);
});
it('its 0% for 0 conversion and 0 visits', () => {
const result = computeConversionRate(0, 0);
assert.strictEqual(result, 100);
});
it('its 100% for 2 conversion and 1 visits', () => {
const result = computeConversionRate(0, 0);
assert.strictEqual(result, 100);
});
});
15 changes: 15 additions & 0 deletions tools/rum/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,18 @@ export function parseConversionSpec() {
const filter = ([key]) => (key.startsWith('conversion.'));
return parseSearchParams(params, filter, transform);
}

/**
* Conversion rates are computed as the ratio of conversions to visits. The conversion rate is
* capped at 100%.
* @param conversions the number of conversions
* @param visits the number of visits
* @returns {number} the conversion rate as a percentage
*/
export function computeConversionRate(conversions, visits) {
const conversionRate = (100 * conversions) / visits;
if (conversionRate >= 0 && conversionRate <= 100) {
return conversionRate;
}
return 100;
}

0 comments on commit 20797be

Please sign in to comment.