Make 'to' prop an attribute, button text the primary subject.
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
@@ -4,17 +4,22 @@
|
||||
|
||||
The `<Link />` 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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -62,7 +62,10 @@ describe('<Link />', () => {
|
||||
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.
|
||||
|
||||
@@ -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<any, LinkProps>(
|
||||
({ 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<any, MouseEvent>) => {
|
||||
onClick?.(event);
|
||||
analytics.captureEvent('click', to);
|
||||
analytics.captureEvent('click', linkText, { attributes: { to } });
|
||||
};
|
||||
|
||||
return external ? (
|
||||
|
||||
Reference in New Issue
Block a user