Skip to content

Commit 3ccad9e

Browse files
authored
feat(nav): add analytics tracking (#80512)
Adds `trackAnalytics` call for primary nav items on the new sidebar
1 parent 2849ca8 commit 3ccad9e

File tree

4 files changed

+77
-8
lines changed

4 files changed

+77
-8
lines changed

static/app/components/nav/config.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export function createNavConfig({organization}: {organization: Organization}): N
5252
label: t('Insights'),
5353
icon: <IconGraph />,
5454
feature: {features: 'insights-entry-points'},
55+
analyticsKey: 'insights',
5556
submenu: [
5657
{
5758
label: MODULE_TITLE_HTTP,
@@ -94,6 +95,7 @@ export function createNavConfig({organization}: {organization: Organization}): N
9495
const perf: NavSidebarItem = {
9596
label: t('Perf.'),
9697
to: '/performance/',
98+
analyticsKey: 'performance',
9799
icon: <IconLightning />,
98100
feature: {
99101
features: 'performance-view',
@@ -104,6 +106,7 @@ export function createNavConfig({organization}: {organization: Organization}): N
104106
const perfDomainViews: NavSidebarItem = {
105107
label: t('Perf.'),
106108
icon: <IconLightning />,
109+
analyticsKey: 'insights-domains',
107110
feature: {features: ['insights-domain-view', 'performance-view']},
108111
submenu: [
109112
{
@@ -130,6 +133,7 @@ export function createNavConfig({organization}: {organization: Organization}): N
130133
{
131134
label: t('Issues'),
132135
icon: <IconIssues />,
136+
analyticsKey: 'issues',
133137
submenu: [
134138
{
135139
label: t('All'),
@@ -154,10 +158,16 @@ export function createNavConfig({organization}: {organization: Organization}): N
154158
{label: t('Feedback'), to: `/${prefix}/feedback/`},
155159
],
156160
},
157-
{label: t('Projects'), to: `/${prefix}/projects/`, icon: <IconProject />},
161+
{
162+
label: t('Projects'),
163+
analyticsKey: 'projects',
164+
to: `/${prefix}/projects/`,
165+
icon: <IconProject />,
166+
},
158167
{
159168
label: t('Explore'),
160169
icon: <IconSearch />,
170+
analyticsKey: 'explore',
161171
submenu: [
162172
{
163173
label: t('Traces'),
@@ -201,6 +211,7 @@ export function createNavConfig({organization}: {organization: Organization}): N
201211
...(hasPerfDomainViews ? [perfDomainViews, perf] : [insights, perf]),
202212
{
203213
label: t('Boards'),
214+
analyticsKey: 'customizable-dashboards',
204215
to: '/dashboards/',
205216
icon: <IconDashboard />,
206217
feature: {
@@ -209,12 +220,18 @@ export function createNavConfig({organization}: {organization: Organization}): N
209220
requireAll: false,
210221
},
211222
},
212-
{label: t('Alerts'), to: `/${prefix}/alerts/rules/`, icon: <IconSiren />},
223+
{
224+
label: t('Alerts'),
225+
analyticsKey: 'alerts',
226+
to: `/${prefix}/alerts/rules/`,
227+
icon: <IconSiren />,
228+
},
213229
],
214230
footer: [
215231
{
216232
label: t('Help'),
217233
icon: <IconQuestion />,
234+
analyticsKey: 'help',
218235
dropdown: [
219236
{
220237
key: 'search',
@@ -242,6 +259,7 @@ export function createNavConfig({organization}: {organization: Organization}): N
242259
},
243260
{
244261
label: t('Settings'),
262+
analyticsKey: 'settings',
245263
to: `/settings/${organization.slug}/`,
246264
icon: <IconSettings />,
247265
},

static/app/components/nav/index.spec.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@ import {LocationFixture} from 'sentry-fixture/locationFixture';
22
import {OrganizationFixture} from 'sentry-fixture/organization';
33
import {RouterFixture} from 'sentry-fixture/routerFixture';
44

5-
import {getAllByRole, render, screen} from 'sentry-test/reactTestingLibrary';
5+
import {trackAnalytics} from 'sentry/utils/analytics';
6+
7+
jest.mock('sentry/utils/analytics', () => ({
8+
trackAnalytics: jest.fn(),
9+
}));
10+
11+
import {getAllByRole, render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
612

713
import Nav from 'sentry/components/nav';
814

@@ -165,4 +171,26 @@ describe('Nav', function () {
165171
});
166172
});
167173
});
174+
175+
describe('analytics', function () {
176+
beforeEach(() => {
177+
render(<Nav />, {
178+
router: RouterFixture({
179+
location: LocationFixture({pathname: '/organizations/org-slug/traces/'}),
180+
}),
181+
organization: OrganizationFixture({features: ALL_AVAILABLE_FEATURES}),
182+
});
183+
});
184+
185+
it('tracks primary sidebar item', async function () {
186+
const issues = screen.getByRole('link', {name: 'Issues'});
187+
await userEvent.click(issues);
188+
expect(trackAnalytics).toHaveBeenCalledWith(
189+
'growth.clicked_sidebar',
190+
expect.objectContaining({
191+
item: 'issues',
192+
})
193+
);
194+
});
195+
});
168196
});

static/app/components/nav/sidebar.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import {Fragment} from 'react';
1+
import type {MouseEventHandler} from 'react';
2+
import {Fragment, useCallback} from 'react';
23
import styled from '@emotion/styled';
34

45
import Feature from 'sentry/components/acl/feature';
@@ -18,8 +19,10 @@ import {
1819
} from 'sentry/components/nav/utils';
1920
import SidebarDropdown from 'sentry/components/sidebar/sidebarDropdown';
2021
import {space} from 'sentry/styles/space';
22+
import {trackAnalytics} from 'sentry/utils/analytics';
2123
import theme from 'sentry/utils/theme';
2224
import {useLocation} from 'sentry/utils/useLocation';
25+
import useOrganization from 'sentry/utils/useOrganization';
2326

2427
function Sidebar() {
2528
return (
@@ -93,19 +96,28 @@ const SidebarItemList = styled('ul')`
9396

9497
interface SidebarItemProps {
9598
item: NavSidebarItem;
99+
children?: React.ReactNode;
100+
onClick?: MouseEventHandler<HTMLElement>;
96101
}
97102

98103
function SidebarItem({item}: SidebarItemProps) {
99104
const to = resolveNavItemTo(item);
100105
const SidebarChild = to ? SidebarLink : SidebarMenu;
106+
const organization = useOrganization();
101107

102108
const FeatureGuard = item.feature ? Feature : Fragment;
103109
const featureGuardProps: any = item.feature ?? {};
104110

111+
const recordAnalytics = useCallback(
112+
() =>
113+
trackAnalytics('growth.clicked_sidebar', {item: item.analyticsKey, organization}),
114+
[organization, item.analyticsKey]
115+
);
116+
105117
return (
106118
<FeatureGuard {...featureGuardProps}>
107119
<SidebarItemWrapper>
108-
<SidebarChild item={item} key={item.label}>
120+
<SidebarChild item={item} key={item.label} onClick={recordAnalytics}>
109121
{item.icon}
110122
<span>{item.label}</span>
111123
</SidebarChild>
@@ -127,7 +139,7 @@ const NavButton = styled('button')`
127139
${linkStyles}
128140
`;
129141

130-
function SidebarLink({children, item}: SidebarItemProps & {children: React.ReactNode}) {
142+
function SidebarLink({children, item, onClick}: SidebarItemProps) {
131143
const location = useLocation();
132144
const isActive = isNavItemActive(item, location);
133145
const isSubmenuActive = isSubmenuItemActive(item, location);
@@ -142,6 +154,7 @@ function SidebarLink({children, item}: SidebarItemProps & {children: React.React
142154
return (
143155
<NavLink
144156
{...linkProps}
157+
onClick={onClick}
145158
className={isActive || isSubmenuActive ? 'active' : undefined}
146159
aria-current={isActive ? 'page' : undefined}
147160
>
@@ -151,7 +164,7 @@ function SidebarLink({children, item}: SidebarItemProps & {children: React.React
151164
);
152165
}
153166

154-
function SidebarMenu({item, children}: SidebarItemProps & {children: React.ReactNode}) {
167+
function SidebarMenu({item, children, onClick}: SidebarItemProps) {
155168
if (!item.dropdown) {
156169
throw new Error(
157170
`Nav item "${item.label}" must have either a \`dropdown\` or \`to\` value!`
@@ -162,7 +175,13 @@ function SidebarMenu({item, children}: SidebarItemProps & {children: React.React
162175
position="right-end"
163176
trigger={(props, isOpen) => {
164177
return (
165-
<NavButton {...props}>
178+
<NavButton
179+
{...props}
180+
onClick={event => {
181+
onClick?.(event);
182+
props.onClick?.(event);
183+
}}
184+
>
166185
<InteractionStateLayer hasSelectedBackground={isOpen} />
167186
{children}
168187
</NavButton>

static/app/components/nav/utils.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export interface NavItemLayout<Item extends NavSidebarItem | NavSubmenuItem> {
3333
* SidebarItem is a top-level NavItem which is always displayed in the app sidebar
3434
*/
3535
export interface NavSidebarItem extends NavItem {
36+
/**
37+
* A unique identifier string, used as a key for analytics
38+
*/
39+
analyticsKey: string;
3640
/**
3741
* The icon to render in the sidebar
3842
*/

0 commit comments

Comments
 (0)