Skip to content

Commit d00c80c

Browse files
authored
feat(nav): Add analytics for secondary items (#87249)
- Adds `navigation.secondary_item_clicked` analytic event for secondary nav items - Renames primary analytic event from `growth.sidebar_clicked` to `navigation.primary_item_clicked`
1 parent 1d7a49a commit d00c80c

File tree

11 files changed

+82
-27
lines changed

11 files changed

+82
-27
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ describe('Nav', function () {
196196
const issues = screen.getByRole('link', {name: 'Issues'});
197197
await userEvent.click(issues);
198198
expect(trackAnalytics).toHaveBeenCalledWith(
199-
'growth.clicked_sidebar',
199+
'navigation.primary_item_clicked',
200200
expect.objectContaining({
201201
item: 'issues',
202202
})

static/app/components/nav/issueViews/issueViewNavItemContent.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ export function IssueViewNavItemContent({
207207
});
208208
}
209209
}}
210+
analyticsItemName="issues_view_starred"
210211
>
211212
<IssueViewNavEditableTitle
212213
label={view.label}

static/app/components/nav/primary/components.tsx

+12-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {type MouseEventHandler, useCallback} from 'react';
1+
import type {MouseEventHandler} from 'react';
22
import {css, type Theme} from '@emotion/react';
33
import styled from '@emotion/styled';
44

@@ -12,6 +12,7 @@ import {isLinkActive, makeLinkPropsFromTo} from 'sentry/components/nav/utils';
1212
import {Tooltip} from 'sentry/components/tooltip';
1313
import {IconDefaultsProvider} from 'sentry/icons/useIconDefaults';
1414
import {space} from 'sentry/styles/space';
15+
import type {Organization} from 'sentry/types/organization';
1516
import {trackAnalytics} from 'sentry/utils/analytics';
1617
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
1718
import {useLocation} from 'sentry/utils/useLocation';
@@ -43,6 +44,13 @@ interface SidebarButtonProps {
4344
onClick?: MouseEventHandler<HTMLElement>;
4445
}
4546

47+
function recordPrimaryItemClick(analyticsKey: string, organization: Organization) {
48+
trackAnalytics('navigation.primary_item_clicked', {
49+
item: analyticsKey,
50+
organization,
51+
});
52+
}
53+
4654
export function SidebarItem({
4755
children,
4856
...props
@@ -63,10 +71,6 @@ export function SidebarMenu({
6371
forceLabel,
6472
}: SidebarItemDropdownProps) {
6573
const organization = useOrganization();
66-
const recordAnalytics = useCallback(
67-
() => trackAnalytics('growth.clicked_sidebar', {item: analyticsKey, organization}),
68-
[organization, analyticsKey]
69-
);
7074
const {layout} = useNavContext();
7175

7276
const showLabel = forceLabel || layout === NavLayout.MOBILE;
@@ -82,7 +86,7 @@ export function SidebarMenu({
8286
{...props}
8387
aria-label={showLabel ? undefined : label}
8488
onClick={event => {
85-
recordAnalytics();
89+
recordPrimaryItemClick(analyticsKey, organization);
8690
props.onClick?.(event);
8791
}}
8892
isMobile={layout === NavLayout.MOBILE}
@@ -115,17 +119,12 @@ export function SidebarLink({
115119
const {layout} = useNavContext();
116120
const showLabel = forceLabel || layout === NavLayout.MOBILE;
117121

118-
const recordAnalytics = useCallback(
119-
() => trackAnalytics('growth.clicked_sidebar', {item: analyticsKey, organization}),
120-
[organization, analyticsKey]
121-
);
122-
123122
return (
124123
<SidebarItem>
125124
<Tooltip title={label} disabled={showLabel} position="right" skipWrapper>
126125
<NavLink
127126
{...linkProps}
128-
onClick={recordAnalytics}
127+
onClick={() => recordPrimaryItemClick(analyticsKey, organization)}
129128
aria-selected={isActive}
130129
aria-current={isActive ? 'page' : undefined}
131130
aria-label={showLabel ? undefined : label}
@@ -157,7 +156,7 @@ export function SidebarButton({
157156
isMobile={layout === NavLayout.MOBILE}
158157
aria-label={showLabel ? undefined : label}
159158
onClick={(e: React.MouseEvent<HTMLElement>) => {
160-
trackAnalytics('growth.clicked_sidebar', {item: analyticsKey, organization});
159+
recordPrimaryItemClick(analyticsKey, organization);
161160
buttonProps.onClick?.(e);
162161
onClick?.(e);
163162
}}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function PrimaryNavigationItems() {
7575
<SidebarLink
7676
to={`/${prefix}/dashboards/`}
7777
activeTo={`/${prefix}/dashboard`}
78-
analyticsKey="customizable-dashboards"
78+
analyticsKey="dashboards"
7979
label={NAV_GROUP_LABELS[PrimaryNavGroup.DASHBOARDS]}
8080
>
8181
<IconDashboard />
@@ -86,7 +86,7 @@ export function PrimaryNavigationItems() {
8686
<SidebarLink
8787
to={`/${prefix}/insights/frontend/`}
8888
activeTo={`/${prefix}/insights`}
89-
analyticsKey="insights-domains"
89+
analyticsKey="insights"
9090
label={NAV_GROUP_LABELS[PrimaryNavGroup.INSIGHTS]}
9191
>
9292
<IconGraph type="area" />

static/app/components/nav/secondary.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import {isLinkActive} from 'sentry/components/nav/utils';
1313
import {IconChevron} from 'sentry/icons';
1414
import {t} from 'sentry/locale';
1515
import {space} from 'sentry/styles/space';
16+
import {trackAnalytics} from 'sentry/utils/analytics';
1617
import {useLocation} from 'sentry/utils/useLocation';
18+
import useOrganization from 'sentry/utils/useOrganization';
1719

1820
type SecondaryNavProps = {
1921
children: ReactNode;
@@ -27,6 +29,7 @@ interface SecondaryNavItemProps extends Omit<LinkProps, 'ref' | 'to'> {
2729
* Will display the link as active under the given path.
2830
*/
2931
activeTo?: To;
32+
analyticsItemName?: string;
3033
/**
3134
* When passed, will not show the link as active for descendant paths.
3235
* Same as the RR6 `NavLink` `end` prop.
@@ -103,6 +106,7 @@ SecondaryNav.Section = function SecondaryNavSection({
103106
};
104107

105108
SecondaryNav.Item = function SecondaryNavItem({
109+
analyticsItemName,
106110
children,
107111
to,
108112
activeTo = to,
@@ -112,6 +116,7 @@ SecondaryNav.Item = function SecondaryNavItem({
112116
trailingItems,
113117
...linkProps
114118
}: SecondaryNavItemProps) {
119+
const organization = useOrganization();
115120
const location = useLocation();
116121
const isActive = incomingIsActive ?? isLinkActive(activeTo, location.pathname, {end});
117122

@@ -124,6 +129,14 @@ SecondaryNav.Item = function SecondaryNavItem({
124129
aria-current={isActive ? 'page' : undefined}
125130
aria-selected={isActive}
126131
layout={layout}
132+
onClick={() => {
133+
if (analyticsItemName) {
134+
trackAnalytics('navigation.secondary_item_clicked', {
135+
item: analyticsItemName,
136+
organization,
137+
});
138+
}
139+
}}
127140
>
128141
{leadingItems}
129142
<InteractionStateLayer data-isl hasSelectedBackground={isActive} />

static/app/utils/analytics/navigationAnalyticsEvents.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
type NavigationItemClicked = {
2+
item: string;
3+
};
4+
15
export type NavigationEventParameters = {
26
'navigation.help_menu_opt_in_stacked_navigation_clicked': Record<string, unknown>;
37
'navigation.help_menu_opt_out_stacked_navigation_clicked': Record<string, unknown>;
8+
'navigation.primary_item_clicked': NavigationItemClicked;
9+
'navigation.secondary_item_clicked': NavigationItemClicked;
410
};
511

612
export type NavigationEventKey = keyof NavigationEventParameters;
@@ -10,4 +16,6 @@ export const navigationAnalyticsEventMap: Record<NavigationEventKey, string | nu
1016
'Navigation: Help Menu Opt In To Stacked Navigation Clicked',
1117
'navigation.help_menu_opt_out_stacked_navigation_clicked':
1218
'Navigation: Help Menu Opt Out Of Stacked Navigation Clicked',
19+
'navigation.primary_item_clicked': 'Navigation: Primary Item Clicked',
20+
'navigation.secondary_item_clicked': 'Navigation: Secondary Item Clicked',
1321
};

static/app/views/dashboards/navigation.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function DashboardsSecondaryNav({children}: DashboardsNavigationProps) {
3939
</SecondaryNav.Header>
4040
<SecondaryNav.Body>
4141
<SecondaryNav.Section>
42-
<SecondaryNav.Item to={`${baseUrl}/`} end>
42+
<SecondaryNav.Item to={`${baseUrl}/`} end analyticsItemName="dashboards_all">
4343
{t('All')}
4444
</SecondaryNav.Item>
4545
</SecondaryNav.Section>
@@ -48,6 +48,7 @@ function DashboardsSecondaryNav({children}: DashboardsNavigationProps) {
4848
<SecondaryNav.Item
4949
key={dashboard.id}
5050
to={`/organizations/${organization.slug}/dashboard/${dashboard.id}/`}
51+
analyticsItemName="dashboard_starred_item"
5152
>
5253
{dashboard.title}
5354
</SecondaryNav.Item>

static/app/views/explore/navigation.tsx

+20-5
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,47 @@ export default function ExploreNavigation({children}: Props) {
3232
<SecondaryNav.Body>
3333
<SecondaryNav.Section>
3434
<Feature features="performance-trace-explorer">
35-
<SecondaryNav.Item to={`${baseUrl}/traces/`}>
35+
<SecondaryNav.Item
36+
to={`${baseUrl}/traces/`}
37+
analyticsItemName="explore_traces"
38+
>
3639
{t('Traces')}
3740
</SecondaryNav.Item>
3841
</Feature>
3942
<Feature features="ourlogs-enabled">
40-
<SecondaryNav.Item to={`${baseUrl}/logs/`}>{t('Logs')}</SecondaryNav.Item>
43+
<SecondaryNav.Item to={`${baseUrl}/logs/`} analyticsItemName="explore_logs">
44+
{t('Logs')}
45+
</SecondaryNav.Item>
4146
</Feature>
4247
<Feature features="profiling">
43-
<SecondaryNav.Item to={`${baseUrl}/profiling/`}>
48+
<SecondaryNav.Item
49+
to={`${baseUrl}/profiling/`}
50+
analyticsItemName="explore_profiles"
51+
>
4452
{t('Profiles')}
4553
</SecondaryNav.Item>
4654
</Feature>
4755
<Feature features="session-replay-ui">
48-
<SecondaryNav.Item to={`${baseUrl}/replays/`}>
56+
<SecondaryNav.Item
57+
to={`${baseUrl}/replays/`}
58+
analyticsItemName="explore_replays"
59+
>
4960
{t('Replays')}
5061
</SecondaryNav.Item>
5162
</Feature>
5263
<Feature features="discover-basic">
5364
<SecondaryNav.Item
5465
to={`${baseUrl}/discover/homepage/`}
5566
activeTo={`${baseUrl}/discover/`}
67+
analyticsItemName="explore_discover"
5668
>
5769
{t('Discover')}
5870
</SecondaryNav.Item>
5971
</Feature>
60-
<SecondaryNav.Item to={`${baseUrl}/releases/`}>
72+
<SecondaryNav.Item
73+
to={`${baseUrl}/releases/`}
74+
analyticsItemName="explore_releases"
75+
>
6176
{t('Releases')}
6277
</SecondaryNav.Item>
6378
</SecondaryNav.Section>

static/app/views/insights/navigation.tsx

+16-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ function InsightsSecondaryNav({children}: InsightsNavigationProps) {
8585
</SecondaryNav.Header>
8686
<SecondaryNav.Body>
8787
<SecondaryNav.Section>
88-
<SecondaryNav.Item to={`${baseUrl}/${FRONTEND_LANDING_SUB_PATH}/`}>
88+
<SecondaryNav.Item
89+
to={`${baseUrl}/${FRONTEND_LANDING_SUB_PATH}/`}
90+
analyticsItemName="insights_frontend"
91+
>
8992
{FRONTEND_SIDEBAR_LABEL}
9093
</SecondaryNav.Item>
9194
<SecondaryNav.Item
@@ -98,14 +101,19 @@ function InsightsSecondaryNav({children}: InsightsNavigationProps) {
98101
(!isStarredProjectSelected || !isLaravelInsightsEnabled)
99102
}
100103
to={`${baseUrl}/${BACKEND_LANDING_SUB_PATH}/`}
104+
analyticsItemName="insights_backend"
101105
>
102106
{BACKEND_SIDEBAR_LABEL}
103107
</SecondaryNav.Item>
104-
<SecondaryNav.Item to={`${baseUrl}/${MOBILE_LANDING_SUB_PATH}/`}>
108+
<SecondaryNav.Item
109+
to={`${baseUrl}/${MOBILE_LANDING_SUB_PATH}/`}
110+
analyticsItemName="insights_mobile"
111+
>
105112
{MOBILE_SIDEBAR_LABEL}
106113
</SecondaryNav.Item>
107114
<SecondaryNav.Item
108115
to={`${baseUrl}/${AI_LANDING_SUB_PATH}/${MODULE_BASE_URLS[ModuleName.AI]}/`}
116+
analyticsItemName="insights_ai"
109117
>
110118
{AI_SIDEBAR_LABEL}
111119
</SecondaryNav.Item>
@@ -136,11 +144,16 @@ function InsightsSecondaryNav({children}: InsightsNavigationProps) {
136144
projectPlatforms={project.platform ? [project.platform] : ['default']}
137145
/>
138146
}
147+
analyticsItemName="insights_project_starred"
139148
>
140149
{project.slug}
141150
</SecondaryNav.Item>
142151
))}
143-
<SecondaryNav.Item to={`${baseUrl}/projects/`} end>
152+
<SecondaryNav.Item
153+
to={`${baseUrl}/projects/`}
154+
end
155+
analyticsItemName="insights_projects_all"
156+
>
144157
{t('All Projects')}
145158
</SecondaryNav.Item>
146159
</SecondaryNav.Section>

static/app/views/issues/navigation.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,13 @@ export function IssueNavigation({children}: IssuesWrapperProps) {
5050
<SecondaryNav.Header>{t('Issues')}</SecondaryNav.Header>
5151
<SecondaryNav.Body>
5252
<SecondaryNav.Section>
53-
<SecondaryNav.Item to={`${baseUrl}/`} end>
53+
<SecondaryNav.Item to={`${baseUrl}/`} end analyticsItemName="issues_feed">
5454
{t('Feed')}
5555
</SecondaryNav.Item>
56-
<SecondaryNav.Item to={`${baseUrl}/feedback/`}>
56+
<SecondaryNav.Item
57+
to={`${baseUrl}/feedback/`}
58+
analyticsItemName="issues_feedback"
59+
>
5760
{t('Feedback')}
5861
</SecondaryNav.Item>
5962
</SecondaryNav.Section>
@@ -98,6 +101,7 @@ export function IssueNavigation({children}: IssuesWrapperProps) {
98101
<SecondaryNav.Item
99102
to={`${baseUrl}/alerts/rules/`}
100103
activeTo={`${baseUrl}/alerts/`}
104+
analyticsItemName="issues_alerts"
101105
>
102106
{t('Alerts')}
103107
</SecondaryNav.Item>

static/app/views/settings/components/settingsNavItem.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ function SettingsNavItem({badge, label, id, to, index, ...props}: Props) {
4848
to={to}
4949
end={index}
5050
trailingItems={badge ? <SettingsNavBadge badge={badge} /> : null}
51+
analyticsItemName={id ? `settings_${id}` : undefined}
5152
{...props}
5253
>
5354
<LabelHook id={id}>{label}</LabelHook>

0 commit comments

Comments
 (0)