Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance(telemetry): measure BCD table views, link clicks, cell expansions #12030

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a06721c
enhance(telemetry): measure BCD table views + clicks
caugner Oct 24, 2024
057446e
refactor(bcd): extract getCurrentStatus()
caugner Oct 24, 2024
11ce0f6
refactor(bcd): extract getStatus()
caugner Oct 24, 2024
ddca183
refactor(bcd): extract getCurrentSupportType()
caugner Oct 24, 2024
8aca71d
refactor(bcd): extract SupportType
caugner Oct 24, 2024
f1b1653
refactor(bcd): extract getCurrentSupportAttributes()
caugner Oct 24, 2024
22f4131
fixup! refactor(bcd): extract getCurrentSupportType()
caugner Oct 24, 2024
b327895
enhance(telemetry): measure BCD support {type,attrs}
caugner Oct 24, 2024
8a60430
chore(telemetry): use "bcd:" for BCD table
caugner Oct 24, 2024
c7ff980
fixup! enhance(telemetry): measure BCD support {type,attrs}
caugner Oct 24, 2024
a4f419d
enhance(telemetry): measure links in BCD table
caugner Oct 24, 2024
473ff5d
chore(bcd): record query on view
caugner Oct 28, 2024
1d36f8d
fix(bcd): observe header, not full table
caugner Oct 28, 2024
16a1f26
Add intersectionObserverOptions for useViewed
fiji-flo Oct 29, 2024
4b87dad
Revert "fix(bcd): observe header, not full table"
caugner Oct 30, 2024
143719f
fix(bcd): measure any partial view
caugner Oct 30, 2024
2560d79
fix(hooks): disable IntersectionObserver on view
caugner Oct 30, 2024
d9df733
enhance(bcd): add "removed" support type
caugner Oct 30, 2024
3f22122
refactor(bcd): simplify getSupportType()
caugner Oct 30, 2024
ef0532d
Revert "refactor(bcd): simplify getSupportType()"
caugner Nov 4, 2024
85fa1a6
Revert "enhance(bcd): add "removed" support type"
caugner Nov 4, 2024
fe3c4ef
Revert "fixup! refactor(bcd): extract getCurrentSupportType()"
caugner Nov 4, 2024
6d983d1
Revert "refactor(bcd): extract getCurrentSupportAttributes()"
caugner Nov 4, 2024
e5d4c55
Revert "refactor(bcd): extract SupportType"
caugner Nov 4, 2024
49af2b4
Revert "refactor(bcd): extract getCurrentSupportType()"
caugner Nov 4, 2024
3c6d272
Revert "refactor(bcd): extract getStatus()"
caugner Nov 4, 2024
b38a264
Revert "refactor(bcd): extract getCurrentStatus()"
caugner Nov 4, 2024
2b9c595
chore(bcd): duplicate support {type,attrs} calculation inline
caugner Nov 4, 2024
1b8dad9
Merge branch 'main' into MP-1619-measure-bcd-table-usage
caugner Nov 4, 2024
a68d282
fixup! chore(bcd): duplicate support {type,attrs} calculation inline
caugner Nov 4, 2024
92ac2be
chore(bcd): move dispatchCellToggle() up to return early
caugner Nov 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,63 @@ import {
isTruthy,
versionIsPreview,
SupportStatementExtended,
SupportType,
bugURLToString,
SimpleSupportStatementExtended,
} from "./utils";
import { LEGEND_LABELS } from "./legend";
import { DEFAULT_LOCALE } from "../../../../../libs/constants";
import { BCD_TABLE } from "../../../telemetry/constants";

function getSupportClassName(
export function getCurrentSupportType(
support: SupportStatementExtended | undefined,
browser: BCD.BrowserStatement
): "no" | "yes" | "partial" | "preview" | "removed-partial" | "unknown" {
): SupportType {
if (!support) {
return "unknown";
}

let { flags, version_added, version_removed, partial_implementation } =
getCurrentSupport(support)!;
const currentSupport = getCurrentSupport(support)!;

return getSupportType(currentSupport, browser);
}

function getSupportType(
caugner marked this conversation as resolved.
Show resolved Hide resolved
support: SimpleSupportStatementExtended,
browser: BCD.BrowserStatement
): SupportType {
const { flags, version_added, version_removed, partial_implementation } =
support;

let className;
if (version_added === null) {
className = "unknown";
return "unknown";
} else if (versionIsPreview(version_added, browser)) {
className = "preview";
return "preview";
} else if (version_added) {
className = "yes";
if (version_removed || (flags && flags.length)) {
className = "no";
if (version_removed) {
if (partial_implementation) {
return "removed-partial";
} else {
return "removed";
}
} else if (flags && flags.length) {
return "no";
} else if (partial_implementation) {
return "partial";
} else {
return "yes";
}
} else {
className = "no";
return "no";
}
if (partial_implementation) {
className = version_removed ? "removed-partial" : "partial";
}

function getSupportClassName(supportType: SupportType): string {
caugner marked this conversation as resolved.
Show resolved Hide resolved
if (supportType === "removed") {
return "no";
}

return className;
return supportType;
}

function getSupportBrowserReleaseDate(
Expand Down Expand Up @@ -123,6 +146,52 @@ function versionLabelFromSupport(
);
}

function getCurrentStatus(
support: BCD.SupportStatement | undefined,
supportType: SupportType,
browser: BCD.BrowserStatement
) {
const currentSupport = getCurrentSupport(support);

return getStatus(currentSupport, supportType, browser);
}

function getStatus(
currentSupport: SimpleSupportStatementExtended | undefined,
supportType: SupportType,
browser: BCD.BrowserStatement
) {
const added = currentSupport?.version_added ?? null;
const lastVersion = currentSupport?.version_last ?? null;

let status: {
isSupported: SupportType;
label?: React.ReactNode;
};

switch (added) {
case null:
status = { isSupported: "unknown" };
break;
case true:
status = { isSupported: lastVersion ? "no" : "yes" };
break;
case false:
status = { isSupported: "no" };
break;
case "preview":
status = { isSupported: "preview" };
break;
default:
status = {
isSupported: supportType,
label: versionLabelFromSupport(added, lastVersion, browser),
};
break;
}
return status;
}

const CellText = React.memo(
({
support,
Expand All @@ -133,40 +202,10 @@ const CellText = React.memo(
browser: BCD.BrowserStatement;
timeline?: boolean;
}) => {
const currentSupport = getCurrentSupport(support);

const added = currentSupport?.version_added ?? null;
const lastVersion = currentSupport?.version_last ?? null;

const browserReleaseDate = getSupportBrowserReleaseDate(support);
const supportClassName = getSupportClassName(support, browser);

let status:
| { isSupported: "unknown" }
| {
isSupported: "no" | "yes" | "partial" | "preview" | "removed-partial";
label?: React.ReactNode;
};
switch (added) {
case null:
status = { isSupported: "unknown" };
break;
case true:
status = { isSupported: lastVersion ? "no" : "yes" };
break;
case false:
status = { isSupported: "no" };
break;
case "preview":
status = { isSupported: "preview" };
break;
default:
status = {
isSupported: supportClassName,
label: versionLabelFromSupport(added, lastVersion, browser),
};
break;
}
const supportType = getCurrentSupportType(support, browser);
const supportClassName = getSupportClassName(supportType);
const status = getCurrentStatus(support, supportType, browser);

let label: string | React.ReactNode;
let title = "";
Expand All @@ -192,6 +231,7 @@ const CellText = React.memo(
break;

case "no":
case "removed":
title = "No support";
label = status.label || "No";
break;
Expand Down Expand Up @@ -260,22 +300,41 @@ function Icon({ name }: { name: string }) {
}

function CellIcons({ support }: { support: BCD.SupportStatement | undefined }) {
const supportItem = getCurrentSupport(support);
if (!supportItem) {
const attrs = getCurrentSupportAttributes(support);

if (!attrs) {
return null;
}

const icons = [
supportItem.prefix && <Icon key="prefix" name="prefix" />,
hasNoteworthyNotes(supportItem) && <Icon key="footnote" name="footnote" />,
supportItem.alternative_name && <Icon key="altname" name="altname" />,
supportItem.flags && <Icon key="disabled" name="disabled" />,
hasMore(support) && <Icon key="more" name="more" />,
attrs.pre && <Icon key="prefix" name="prefix" />,
attrs.note && <Icon key="footnote" name="footnote" />,
attrs.alt && <Icon key="altname" name="altname" />,
attrs.flag && <Icon key="disabled" name="disabled" />,
attrs.more && <Icon key="more" name="more" />,
].filter(Boolean);

return icons.length ? <div className="bc-icons">{icons}</div> : null;
}

export function getCurrentSupportAttributes(
support: BCD.SimpleSupportStatement | BCD.SimpleSupportStatement[] | undefined
) {
const supportItem = getCurrentSupport(support);

if (!supportItem) {
return null;
}

return {
pre: !!supportItem.prefix,
note: hasNoteworthyNotes(supportItem),
alt: !!supportItem.alternative_name,
flag: !!supportItem.flags,
more: hasMore(support),
};
}

function FlagsNote({
supportItem,
browser,
Expand Down Expand Up @@ -418,7 +477,7 @@ function getNotes(
<React.Fragment key={i}>
<div className="bc-notes-wrapper">
<dt
className={`bc-supports-${getSupportClassName(
className={`bc-supports-${getCurrentSupportType(
item,
browser
)} bc-supports`}
Expand Down Expand Up @@ -462,7 +521,8 @@ function CompatCell({
onToggle: () => void;
locale: string;
}) {
const supportClassName = getSupportClassName(support, browserInfo);
const supportType = getCurrentSupportType(support, browserInfo);
const supportClassName = getSupportClassName(supportType);
// NOTE: 1-5-21, I've forced hasNotes to return true, in order to
// make the details view open all the time.
// Whenever the support statement is complex (array with more than one entry)
Expand Down Expand Up @@ -545,7 +605,11 @@ export const FeatureRow = React.memo(
`/${locale}/docs`
);
titleNode = (
<a href={href} className="bc-table-row-header">
<a
href={href}
className="bc-table-row-header"
data-glean={`${BCD_TABLE}: link -> ${href}`}
>
{title}
{compat.status && <StatusIcons status={compat.status} />}
</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import React, { useReducer } from "react";
import React, { useReducer, useRef } from "react";
import { useLocation } from "react-router-dom";
import type BCD from "@mdn/browser-compat-data/types";
import { BrowserInfoContext } from "./browser-info";
import { BrowserCompatibilityErrorBoundary } from "./error-boundary";
import { FeatureRow } from "./feature-row";
import {
FeatureRow,
getCurrentSupportAttributes,
getCurrentSupportType,
} from "./feature-row";
import { Headers } from "./headers";
import { Legend } from "./legend";
import { listFeatures } from "./utils";
import { useViewed } from "../../../hooks";
import { BCD_TABLE } from "../../../telemetry/constants";
import { useGleanClick } from "../../../telemetry/glean-context";

// Note! Don't import any SCSS here inside *this* component.
// It's done in the component that lazy-loads this component.
Expand Down Expand Up @@ -84,11 +91,15 @@ type CellIndex = [number, number];
function FeatureListAccordion({
features,
browsers,
browserInfo,
locale,
query,
}: {
features: ReturnType<typeof listFeatures>;
browsers: BCD.BrowserName[];
browserInfo: BCD.Browsers;
locale: string;
query: string;
}) {
const [[activeRow, activeColumn], dispatchCellToggle] = useReducer<
React.Reducer<CellIndex | [null, null], CellIndex>
Expand All @@ -100,6 +111,9 @@ function FeatureListAccordion({
[null, null]
);

const gleanClick = useGleanClick();
const clickedCells = useRef(new Set<string>());

return (
<>
{features.map((feature, i) => (
Expand All @@ -109,6 +123,25 @@ function FeatureListAccordion({
index={i}
activeCell={activeRow === i ? activeColumn : null}
onToggleCell={([row, column]: [number, number]) => {
const cell = `${column}:${row}`;
if (!clickedCells.current.has(cell)) {
clickedCells.current.add(cell);
const feature = features[row];
const browser = browsers[column];
const support = feature.compat.support[browser];
const supportType = getCurrentSupportType(
support,
browserInfo[browser]
);
const { ...supportAttributes } =
getCurrentSupportAttributes(support);
const attrs = Object.keys(supportAttributes).filter(
(key) => supportAttributes[key]
);
gleanClick(
`${BCD_TABLE}: click ${browser} ${query} -> ${feature.name} = ${supportType} [${attrs.join(",")}]`
caugner marked this conversation as resolved.
Show resolved Hide resolved
);
}
dispatchCellToggle([row, column]);
}}
locale={locale}
Expand All @@ -130,6 +163,16 @@ export default function BrowserCompatibilityTable({
locale: string;
}) {
const location = useLocation();
const gleanClick = useGleanClick();

const observedNode = useViewed(
() => {
gleanClick(`${BCD_TABLE}: view -> ${query}`);
},
{
threshold: 0,
}
);

if (!data || !Object.keys(data).length) {
throw new Error(
Expand Down Expand Up @@ -177,7 +220,11 @@ export default function BrowserCompatibilityTable({
</a>
<figure className="table-container">
<figure className="table-container-inner">
<table key="bc-table" className="bc-table bc-table-web">
<table
key="bc-table"
className="bc-table bc-table-web"
ref={observedNode}
>
<Headers
platforms={platforms}
browsers={browsers}
Expand All @@ -186,8 +233,10 @@ export default function BrowserCompatibilityTable({
<tbody>
<FeatureListAccordion
browsers={browsers}
browserInfo={browserInfo}
features={listFeatures(data, "", name)}
locale={locale}
query={query}
/>
</tbody>
</table>
Expand Down
Loading
Loading