Capture analytics events for clicks in the graph
Signed-off-by: Oliver Sand <oliver.sand@sda-se.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-graph': patch
|
||||
---
|
||||
|
||||
Capture analytics events for clicks in the graph.
|
||||
@@ -15,12 +15,14 @@
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { analyticsApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
CatalogApi,
|
||||
catalogApiRef,
|
||||
EntityProvider,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { MockAnalyticsApi, renderInTestApp } from '@backstage/test-utils';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { catalogEntityRouteRef, catalogGraphRouteRef } from '../../routes';
|
||||
import { CatalogGraphCard } from './CatalogGraphCard';
|
||||
@@ -117,4 +119,30 @@ describe('<CatalogGraphCard/>', () => {
|
||||
'/catalog-graph?rootEntityRefs%5B%5D=b%3Ad%2Fc',
|
||||
);
|
||||
});
|
||||
|
||||
test('captures analytics event on click', async () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const { findByText } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, analyticsSpy]])}>
|
||||
{wrapper}
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef,
|
||||
'/catalog-graph': catalogGraphRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(await findByText('b:d/c')).toBeInTheDocument();
|
||||
userEvent.click(await findByText('b:d/c'));
|
||||
|
||||
expect(analyticsSpy.getEvents()[0]).toMatchObject({
|
||||
action: 'click',
|
||||
subject: 'b:d/c',
|
||||
attributes: {
|
||||
to: '/entity/{kind}/{namespace}/{name}',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,8 +19,11 @@ import {
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { InfoCard, InfoCardVariants } from '@backstage/core-components';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { useAnalytics, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
formatEntityRefTitle,
|
||||
useEntity,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { makeStyles, Theme } from '@material-ui/core';
|
||||
import qs from 'qs';
|
||||
import React, { MouseEvent, useCallback } from 'react';
|
||||
@@ -78,6 +81,7 @@ export const CatalogGraphCard = ({
|
||||
const catalogGraphRoute = useRouteRef(catalogGraphRouteRef);
|
||||
const navigate = useNavigate();
|
||||
const classes = useStyles({ height });
|
||||
const analytics = useAnalytics();
|
||||
|
||||
const onNodeClick = useCallback(
|
||||
(node: EntityNode, _: MouseEvent<unknown>) => {
|
||||
@@ -87,9 +91,14 @@ export const CatalogGraphCard = ({
|
||||
namespace: nodeEntityName.namespace.toLocaleLowerCase('en-US'),
|
||||
name: nodeEntityName.name,
|
||||
});
|
||||
analytics.captureEvent(
|
||||
'click',
|
||||
node.title ?? formatEntityRefTitle(nodeEntityName),
|
||||
{ attributes: { to: path } },
|
||||
);
|
||||
navigate(path);
|
||||
},
|
||||
[catalogEntityRoute, navigate],
|
||||
[catalogEntityRoute, navigate, analytics],
|
||||
);
|
||||
|
||||
const catalogGraphParams = qs.stringify(
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
import { RELATION_HAS_PART, RELATION_PART_OF } from '@backstage/catalog-model';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { analyticsApiRef } from '@backstage/core-plugin-api';
|
||||
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { MockAnalyticsApi, renderInTestApp } from '@backstage/test-utils';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import React from 'react';
|
||||
import { catalogEntityRouteRef } from '../../routes';
|
||||
@@ -167,4 +168,53 @@ describe('<CatalogGraphPage/>', () => {
|
||||
|
||||
expect(navigate).toBeCalledWith('/entity/{kind}/{namespace}/{name}');
|
||||
});
|
||||
|
||||
test('should capture analytics event when selecting other entity', async () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const { getByText, findAllByTestId } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, analyticsSpy]])}>
|
||||
{wrapper}
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(await findAllByTestId('node')).toHaveLength(2);
|
||||
|
||||
userEvent.click(getByText('b:d/e'));
|
||||
|
||||
expect(analyticsSpy.getEvents()[0]).toMatchObject({
|
||||
action: 'click',
|
||||
subject: 'b:d/e',
|
||||
});
|
||||
});
|
||||
|
||||
test('should capture analytics event when navigating to entity', async () => {
|
||||
const analyticsSpy = new MockAnalyticsApi();
|
||||
const { getByText, findAllByTestId } = await renderInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[analyticsApiRef, analyticsSpy]])}>
|
||||
{wrapper}
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/entity/{kind}/{namespace}/{name}': catalogEntityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(await findAllByTestId('node')).toHaveLength(2);
|
||||
|
||||
userEvent.click(getByText('b:d/e'), { shiftKey: true });
|
||||
|
||||
expect(analyticsSpy.getEvents()[0]).toMatchObject({
|
||||
action: 'click',
|
||||
subject: 'b:d/e',
|
||||
attributes: {
|
||||
to: '/entity/{kind}/{namespace}/{name}',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
Page,
|
||||
SupportButton,
|
||||
} from '@backstage/core-components';
|
||||
import { useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { useAnalytics, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { formatEntityRefTitle } from '@backstage/plugin-catalog-react';
|
||||
import { Grid, makeStyles, Paper, Typography } from '@material-ui/core';
|
||||
import FilterListIcon from '@material-ui/icons/FilterList';
|
||||
@@ -31,11 +31,11 @@ import React, { MouseEvent, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { catalogEntityRouteRef } from '../../routes';
|
||||
import {
|
||||
ALL_RELATION_PAIRS,
|
||||
Direction,
|
||||
EntityNode,
|
||||
EntityRelationsGraph,
|
||||
RelationPairs,
|
||||
ALL_RELATION_PAIRS,
|
||||
} from '../EntityRelationsGraph';
|
||||
import { DirectionFilter } from './DirectionFilter';
|
||||
import { MaxDepthFilter } from './MaxDepthFilter';
|
||||
@@ -133,6 +133,7 @@ export const CatalogGraphPage = ({
|
||||
showFilters,
|
||||
toggleShowFilters,
|
||||
} = useCatalogGraphPage({ initialState });
|
||||
const analytics = useAnalytics();
|
||||
const onNodeClick = useCallback(
|
||||
(node: EntityNode, event: MouseEvent<unknown>) => {
|
||||
const nodeEntityName = parseEntityRef(node.id);
|
||||
@@ -143,12 +144,22 @@ export const CatalogGraphPage = ({
|
||||
namespace: nodeEntityName.namespace.toLocaleLowerCase('en-US'),
|
||||
name: nodeEntityName.name,
|
||||
});
|
||||
|
||||
analytics.captureEvent(
|
||||
'click',
|
||||
node.title ?? formatEntityRefTitle(nodeEntityName),
|
||||
{ attributes: { to: path } },
|
||||
);
|
||||
navigate(path);
|
||||
} else {
|
||||
analytics.captureEvent(
|
||||
'click',
|
||||
node.title ?? formatEntityRefTitle(nodeEntityName),
|
||||
);
|
||||
setRootEntityNames([nodeEntityName]);
|
||||
}
|
||||
},
|
||||
[catalogEntityRoute, navigate, setRootEntityNames],
|
||||
[catalogEntityRoute, navigate, setRootEntityNames, analytics],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user