diff --git a/.changeset/analytics-haunts-dismembered-constellations.md b/.changeset/analytics-haunts-dismembered-constellations.md
index 851527d852..e7cfc52ce1 100644
--- a/.changeset/analytics-haunts-dismembered-constellations.md
+++ b/.changeset/analytics-haunts-dismembered-constellations.md
@@ -4,17 +4,22 @@
The `` component now automatically instruments all link clicks using
the new Analytics API. Each click triggers a `click` event, containing the
-location the user clicked to. In addition, these events inherit plugin-level
-metadata, allowing clicks to be attributed to the plugin containing the link:
+text of the link the user clicked on, as well as the location to which the user
+clicked. In addition, these events inherit plugin/extension-level metadata,
+allowing clicks to be attributed to the plugin/extension/route containing the
+link:
```json
{
"action": "click",
- "subject": "/value/of-the/to-prop/passed-to-the-link",
+ "subject": "Text content of the link that was clicked",
+ "attributes": {
+ "to": "/value/of-the/to-prop/passed-to-the-link"
+ },
"context": {
- "extension": "SomeAssociatedExtension",
+ "extension": "ExtensionInWhichTheLinkWasClicked",
"pluginId": "plugin-in-which-link-was-clicked",
- "routeRef": "any-associated-route-ref-id"
+ "routeRef": "route-ref-in-which-the-link-was-clicked"
}
}
```
diff --git a/packages/core-components/src/components/Link/Link.test.tsx b/packages/core-components/src/components/Link/Link.test.tsx
index 071b6fcffe..df1bcfac03 100644
--- a/packages/core-components/src/components/Link/Link.test.tsx
+++ b/packages/core-components/src/components/Link/Link.test.tsx
@@ -62,7 +62,10 @@ describe('', () => {
await waitFor(() => {
expect(analyticsApi.getEvents()[0]).toMatchObject({
action: 'click',
- subject: '/test',
+ subject: linkText,
+ attributes: {
+ to: '/test',
+ },
});
// Custom onClick handler should have still been fired too.
diff --git a/packages/core-components/src/components/Link/Link.tsx b/packages/core-components/src/components/Link/Link.tsx
index fcfd2483de..d20773a8bb 100644
--- a/packages/core-components/src/components/Link/Link.tsx
+++ b/packages/core-components/src/components/Link/Link.tsx
@@ -34,6 +34,29 @@ export type LinkProps = MaterialLinkProps &
declare function LinkType(props: LinkProps): JSX.Element;
+/**
+ * Given a react node, try to retrieve its text content.
+ */
+const getNodeText = (node: React.ReactNode): string => {
+ // If the node is an array of children, recurse and join.
+ if (node instanceof Array) {
+ return node.map(getNodeText).join(' ').trim();
+ }
+
+ // If the node is a react element, recurse on its children.
+ if (typeof node === 'object' && node) {
+ return getNodeText((node as React.ReactElement)?.props?.children);
+ }
+
+ // Base case: the node is just text. Return it.
+ if (['string', 'number'].includes(typeof node)) {
+ return String(node);
+ }
+
+ // Base case: just return an empty string.
+ return '';
+};
+
/**
* Thin wrapper on top of material-ui's Link component, which...
* - Makes the Link use react-router
@@ -43,12 +66,13 @@ const ActualLink = React.forwardRef(
({ onClick, ...props }, ref) => {
const analytics = useAnalytics();
const to = String(props.to);
+ const linkText = getNodeText(props.children) || to;
const external = isExternalUri(to);
const newWindow = external && !!/^https?:/.exec(to);
const handleClick = (event: React.MouseEvent) => {
onClick?.(event);
- analytics.captureEvent('click', to);
+ analytics.captureEvent('click', linkText, { attributes: { to } });
};
return external ? (