Kaynağa Gözat

[web] Update navigation events (navbar menu, new project) (#22473)

* Add project-list-events.ts

* Add event `menu-expand`

* Add event `menu-click`

* Add event `new-project-expand`

* Add event `new-project-click`

* Change `menu-clicked-*` events to "`menu-clicked`+trackingKey"

* Add `SplitTestProvider` to header-footer-react

* Convert to `menu-click` events in pug files

* Add sidebar `menu-expand` events

* Fixup formatting after rebase

* Fixup format after merge

* Get location type from `ExtraSegmentations`

* Replace `any` by `ExtraSegmentations['menu-click']['item']`

Co-authored-by: Ilkin Ismailov <ilkin.ismailov@overleaf.com>

* Use `string & {}` trick to type `new-project-click.item`

Co-authored-by: Ilkin Ismailov <ilkin.ismailov@overleaf.com>

* Add `location: 'top-menu'` to Pug events

* Add `menu-expand` tracking event

---------

Co-authored-by: Ilkin Ismailov <ilkin.ismailov@overleaf.com>
GitOrigin-RevId: c290835c9b92a25a758cec94b49103edcdcdffed
Antoine Clausse 1 yıl önce
ebeveyn
işleme
c6506d9a2e

+ 17 - 5
services/web/app/views/layout/navbar-marketing-bootstrap-5.pug

@@ -48,6 +48,10 @@ nav.navbar.navbar-default.navbar-main.navbar-expand-lg(class={
 								aria-expanded="false",
 								data-bs-toggle="dropdown"
 								role="menuitem"
+								event-tracking="menu-expand"
+								event-tracking-mb="true"
+								event-tracking-trigger="click"
+								event-segmentation={"item": "admin", "location": "top-menu"}
 							)
 								| Admin
 								span.caret
@@ -84,6 +88,10 @@ nav.navbar.navbar-default.navbar-main.navbar-expand-lg(class={
 										aria-expanded="false",
 										data-bs-toggle="dropdown"
 										role="menuitem"
+										event-tracking="menu-expand"
+										event-tracking-mb="true"
+										event-tracking-trigger="click"
+										event-segmentation={"item": item.trackingKey, "location": "top-menu"}
 									)
 										| !{translate(item.text)}
 										span.caret
@@ -93,7 +101,7 @@ nav.navbar.navbar-default.navbar-main.navbar-expand-lg(class={
 												+dropdown-menu-divider
 											else if child.isContactUs
 												+dropdown-menu-link-item()(data-ol-open-contact-form-modal="contact-us" data-bs-target="#contactUsModal" href data-bs-toggle="modal")
-													span(event-tracking="menu-clicked-contact" event-tracking-mb="true" event-tracking-trigger="click")
+													span(event-tracking="menu-click" event-tracking-mb="true" event-tracking-trigger="click" event-segmentation={"item": "contact", "location": "top-menu"})
 														| #{translate("contact_us")}
 											else
 												if child.url
@@ -127,22 +135,22 @@ nav.navbar.navbar-default.navbar-main.navbar-expand-lg(class={
 							+nav-item.primary
 								+nav-link(
 									href="/register"
-									event-tracking="menu-clicked-register"
+									event-tracking="menu-click"
 									event-tracking-action="clicked"
 									event-tracking-trigger="click"
 									event-tracking-mb="true"
-									event-segmentation={ page: currentUrl }
+									event-segmentation={ page: currentUrl, item: 'register', location: 'top-menu' }
 								) #{translate('sign_up')}
 
 						// login link
 						+nav-item
 							+nav-link(
 								href="/login"
-								event-tracking="menu-clicked-login"
+								event-tracking="menu-click"
 								event-tracking-action="clicked"
 								event-tracking-trigger="click"
 								event-tracking-mb="true"
-								event-segmentation={ page: currentUrl }
+								event-segmentation={ page: currentUrl, item: 'login', location: 'top-menu' }
 							) #{translate('log_in')}
 
 					// projects link and account menu
@@ -155,6 +163,10 @@ nav.navbar.navbar-default.navbar-main.navbar-expand-lg(class={
 								aria-expanded="false",
 								data-bs-toggle="dropdown"
 								role="menuitem"
+								event-tracking="menu-expand"
+								event-tracking-mb="true"
+								event-tracking-trigger="click"
+								event-segmentation={"item": "account", "location": "top-menu"}
 							)
 								| #{translate('Account')}
 								span.caret

+ 17 - 5
services/web/app/views/layout/navbar-marketing.pug

@@ -42,6 +42,10 @@ nav.navbar.navbar-default.navbar-main
 								aria-haspopup="true",
 								aria-expanded="false",
 								data-toggle="dropdown"
+								event-tracking="menu-expand"
+								event-tracking-mb="true"
+								event-tracking-trigger="click"
+								event-segmentation={"item": "admin", "location": "top-menu"}
 							)
 								| Admin
 								span.caret
@@ -85,6 +89,10 @@ nav.navbar.navbar-default.navbar-main
 										aria-haspopup="true",
 										aria-expanded="false",
 										data-toggle="dropdown"
+										event-tracking="menu-expand"
+										event-tracking-mb="true"
+										event-tracking-trigger="click"
+										event-segmentation={"item": item.trackingKey, "location": "top-menu"}
 									)
 										| !{translate(item.text)}
 										span.caret
@@ -95,7 +103,7 @@ nav.navbar.navbar-default.navbar-main
 											else if child.isContactUs
 												li
 													a(data-ol-open-contact-form-modal="contact-us" href)
-														span(event-tracking="menu-clicked-contact" event-tracking-mb="true" event-tracking-trigger="click")
+														span(event-tracking="menu-click" event-tracking-mb="true" event-tracking-trigger="click" event-segmentation={"item": "contact", "location": "top-menu"})
 															| #{translate("contact_us")}
 											else
 												li
@@ -130,22 +138,22 @@ nav.navbar.navbar-default.navbar-main
 							li.primary
 								a(
 									href="/register"
-									event-tracking="menu-clicked-register"
+									event-tracking="menu-click"
 									event-tracking-action="clicked"
 									event-tracking-trigger="click"
 									event-tracking-mb="true"
-									event-segmentation={ page: currentUrl }
+									event-segmentation={ page: currentUrl, item: 'register', location: 'top-menu' }
 								) #{translate('sign_up')}
 
 						// login link
 						li
 							a(
 								href="/login"
-								event-tracking="menu-clicked-login"
+								event-tracking="menu-click"
 								event-tracking-action="clicked"
 								event-tracking-trigger="click"
 								event-tracking-mb="true"
-								event-segmentation={ page: currentUrl }
+								event-segmentation={ page: currentUrl, item: 'login', location: 'top-menu' }
 							) #{translate('log_in')}
 
 					// projects link and account menu
@@ -159,6 +167,10 @@ nav.navbar.navbar-default.navbar-main
 								aria-haspopup="true",
 								aria-expanded="false",
 								data-toggle="dropdown"
+								event-tracking="menu-expand"
+								event-tracking-mb="true"
+								event-tracking-trigger="click"
+								event-segmentation={"item": "account", "location": "top-menu"}
 							)
 								| #{translate('Account')}
 								span.caret

+ 17 - 5
services/web/app/views/layout/navbar-website-redesign.pug

@@ -42,6 +42,10 @@ nav.navbar.navbar-default.navbar-main.website-redesign-navbar
 								aria-haspopup="true",
 								aria-expanded="false",
 								data-toggle="dropdown"
+								event-tracking="menu-expand"
+								event-tracking-mb="true"
+								event-tracking-trigger="click"
+								event-segmentation={"item": "admin", "location": "top-menu"}
 							)
 								| Admin
 								span.caret
@@ -85,6 +89,10 @@ nav.navbar.navbar-default.navbar-main.website-redesign-navbar
 										aria-haspopup="true",
 										aria-expanded="false",
 										data-toggle="dropdown"
+										event-tracking="menu-expand"
+										event-tracking-mb="true"
+										event-tracking-trigger="click"
+										event-segmentation={"item": item.trackingKey, "location": "top-menu"}
 									)
 										| !{translate(item.text)}
 										span.caret
@@ -95,7 +103,7 @@ nav.navbar.navbar-default.navbar-main.website-redesign-navbar
 											else if child.isContactUs
 												li
 													a(data-ol-open-contact-form-modal="contact-us" href)
-														span(event-tracking="menu-clicked-contact" event-tracking-mb="true" event-tracking-trigger="click")
+														span(event-tracking="menu-click" event-tracking-mb="true" event-tracking-trigger="click" event-segmentation={"item": "contact", "location": "top-menu"})
 															| #{translate("contact_us")}
 											else
 												li
@@ -130,22 +138,22 @@ nav.navbar.navbar-default.navbar-main.website-redesign-navbar
 							li.primary
 								a(
 									href="/register"
-									event-tracking="menu-clicked-register"
+									event-tracking="menu-click"
 									event-tracking-action="clicked"
 									event-tracking-trigger="click"
 									event-tracking-mb="true"
-									event-segmentation={ page: currentUrl }
+									event-segmentation={ page: currentUrl, item: 'register', location: 'top-menu' }
 								) #{translate('sign_up')}
 
 						// login link
 						li.secondary
 							a(
 								href="/login"
-								event-tracking="menu-clicked-login"
+								event-tracking="menu-click"
 								event-tracking-action="clicked"
 								event-tracking-trigger="click"
 								event-tracking-mb="true"
-								event-segmentation={ page: currentUrl }
+								event-segmentation={ page: currentUrl, item: 'login', location: 'top-menu' }
 							) #{translate('log_in')}
 
 					// projects link and account menu
@@ -159,6 +167,10 @@ nav.navbar.navbar-default.navbar-main.website-redesign-navbar
 								aria-haspopup="true",
 								aria-expanded="false",
 								data-toggle="dropdown"
+								event-tracking="menu-expand"
+								event-tracking-mb="true"
+								event-tracking-trigger="click"
+								event-segmentation={"item": "account", "location": "top-menu"}
 							)
 								| #{translate('Account')}
 								span.caret

+ 8 - 0
services/web/frontend/js/features/event-tracking/index.js

@@ -12,6 +12,14 @@ function setupEventTracking(el) {
   const element = el.getAttribute('event-tracking-element')
 
   function submit() {
+    if (key === 'menu-expand') {
+      const expanded = el.getAttribute('aria-expanded')
+      if (expanded === 'true') {
+        // skip if the menu is already expanded
+        return
+      }
+    }
+
     const segmentation = JSON.parse(
       el.getAttribute('event-segmentation') || '{}'
     )

+ 24 - 14
services/web/frontend/js/features/project-list/components/new-project-button.tsx

@@ -17,6 +17,8 @@ import {
   DropdownMenu,
   DropdownToggle,
 } from '@/features/ui/components/bootstrap-5/dropdown-menu'
+import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
+import type { PortalTemplate } from '../../../../../types/portal-template'
 
 type SendTrackingEvent = {
   dropdownMenu: string
@@ -54,7 +56,7 @@ function NewProjectButton({
     useState<Nullable<NewProjectButtonModalVariant>>(null)
   const portalTemplates = getMeta('ol-portalTemplates') || []
   const { show: enableAddAffiliationWidget } = useAddAffiliation()
-
+  const sendProjectListMB = useSendProjectListMB()
   const sendTrackingEvent = useCallback(
     ({
       dropdownMenu,
@@ -103,37 +105,46 @@ function NewProjectButton({
         dropdownMenu: dropdownMenuEvent,
         dropdownOpen: true,
       })
+      sendProjectListMB('new-project-click', { item: dropdownMenuEvent })
 
       setModal(modalVariant)
     },
-    [sendTrackingEvent]
+    [sendProjectListMB, sendTrackingEvent]
   )
 
   const handlePortalTemplateClick = useCallback(
-    (e: React.MouseEvent, institutionTemplateName: string) => {
+    (e: React.MouseEvent, template: PortalTemplate) => {
       // avoid invoking the "onClick" callback on the main dropdown button
       e.stopPropagation()
 
       sendTrackingEvent({
         dropdownMenu: 'institution-template',
         dropdownOpen: true,
-        institutionTemplateName,
+        institutionTemplateName: template.name,
+      })
+      sendProjectListMB('new-project-click', {
+        item: template.name,
+        destinationURL: template.url,
       })
     },
-    [sendTrackingEvent]
+    [sendProjectListMB, sendTrackingEvent]
   )
 
   const handleStaticTemplateClick = useCallback(
-    (e: React.MouseEvent, templateTrackingKey: string) => {
+    (e: React.MouseEvent, template: { trackingKey: string; url: string }) => {
       // avoid invoking the "onClick" callback on the main dropdown button
       e.stopPropagation()
 
       sendTrackingEvent({
-        dropdownMenu: templateTrackingKey,
+        dropdownMenu: template.trackingKey,
         dropdownOpen: true,
       })
+      sendProjectListMB('new-project-click', {
+        item: template.trackingKey,
+        destinationURL: template.url,
+      })
     },
-    [sendTrackingEvent]
+    [sendProjectListMB, sendTrackingEvent]
   )
 
   const [importProjectFromGithubMenu] = importOverleafModules(
@@ -149,6 +160,9 @@ function NewProjectButton({
       <Dropdown
         className={classnames('new-project-dropdown', className)}
         onSelect={handleMainButtonClick}
+        onToggle={nextShow => {
+          if (nextShow) sendProjectListMB('new-project-expand', undefined)
+        }}
       >
         <DropdownToggle
           id={id}
@@ -217,9 +231,7 @@ function NewProjectButton({
                   <DropdownItem
                     key={`portal-template-${index}`}
                     href={`${portalTemplate.url}#templates`}
-                    onClick={e =>
-                      handlePortalTemplateClick(e, portalTemplate.name)
-                    }
+                    onClick={e => handlePortalTemplateClick(e, portalTemplate)}
                     aria-label={`${portalTemplate.name} ${t('template')}`}
                   >
                     {portalTemplate.name}
@@ -241,9 +253,7 @@ function NewProjectButton({
             <li role="none" key={`new-project-button-template-${index}`}>
               <DropdownItem
                 href={templateLink.url}
-                onClick={e =>
-                  handleStaticTemplateClick(e, templateLink.trackingKey)
-                }
+                onClick={e => handleStaticTemplateClick(e, templateLink)}
                 aria-label={`${templateLink.name} ${t('template')}`}
               >
                 {templateLink.name === 'view_all'

+ 54 - 0
services/web/frontend/js/features/project-list/components/project-list-events.ts

@@ -0,0 +1,54 @@
+import { useCallback } from 'react'
+import { useSplitTestContext } from '@/shared/context/split-test-context'
+import { sendMB } from '@/infrastructure/event-tracking'
+
+export type ExtraSegmentations = {
+  'menu-expand': {
+    item: 'help' | 'account' | 'features' | 'admin'
+    location: 'top-menu' | 'sidebar'
+  }
+  'menu-click': {
+    item:
+      | 'login'
+      | 'register'
+      | 'premium-features'
+      | 'enterprises'
+      | 'universities'
+      | 'publishers'
+      | 'edu'
+      | 'government'
+      | 'why-latex'
+      | 'learn'
+      | 'contact'
+      | 'templates'
+      | 'plans'
+    location: 'top-menu' | 'sidebar'
+    destinationURL?: string
+  }
+  'new-project-expand': undefined
+  'new-project-click': {
+    item:
+      | 'blank-project'
+      | 'example-project'
+      | 'upload'
+      | 'github-import'
+      | 'all-templates'
+      | (string & {})
+    destinationURL?: string
+  }
+}
+
+export const useSendProjectListMB = () => {
+  const { splitTestVariants } = useSplitTestContext()
+  const hasDsNav = splitTestVariants['sidebar-navigation-ui-update'] as
+    | 'default'
+    | 'active'
+  return useCallback(
+    <T extends keyof ExtraSegmentations>(
+      event: T,
+      payload: ExtraSegmentations[T]
+    ) =>
+      sendMB(event, { ...payload, 'sidebar-navigation-ui-update': hasDsNav }),
+    [hasDsNav]
+  )
+}

+ 18 - 2
services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx

@@ -16,6 +16,7 @@ import { useContactUsModal } from '@/shared/hooks/use-contact-us-modal'
 import { UserProvider } from '@/shared/context/user-context'
 import { AccountMenuItems } from '@/features/ui/components/bootstrap-5/navbar/account-menu-items'
 import { useScrolled } from '@/features/project-list/components/sidebar/use-scroll'
+import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
 
 function SidebarDsNav() {
   const { t } = useTranslation()
@@ -29,6 +30,7 @@ function SidebarDsNav() {
   const { mousePos, getHandleProps, getTargetProps } = usePersistedResize({
     name: 'project-sidebar',
   })
+  const sendMB = useSendProjectListMB()
   const { sessionUser, showSubscriptionLink, items } = getMeta('ol-navbar')
   const helpItem = items.find(
     item => item.text === 'help'
@@ -65,7 +67,12 @@ function SidebarDsNav() {
           {helpItem && (
             <Dropdown
               className="ds-nav-icon-dropdown"
-              onToggle={show => setShowHelpDropdown(show)}
+              onToggle={show => {
+                setShowHelpDropdown(show)
+                if (show) {
+                  sendMB('menu-expand', { item: 'help', location: 'sidebar' })
+                }
+              }}
             >
               <Dropdown.Toggle role="menuitem" aria-label={t('help')}>
                 <OLTooltip
@@ -92,6 +99,7 @@ function SidebarDsNav() {
                 <NavDropdownMenuItems
                   dropdown={helpItem.dropdown}
                   showContactUsModal={showContactUsModal}
+                  location="sidebar"
                 />
               </Dropdown.Menu>
             </Dropdown>
@@ -100,7 +108,15 @@ function SidebarDsNav() {
             <>
               <Dropdown
                 className="ds-nav-icon-dropdown"
-                onToggle={show => setShowAccountDropdown(show)}
+                onToggle={show => {
+                  setShowAccountDropdown(show)
+                  if (show) {
+                    sendMB('menu-expand', {
+                      item: 'account',
+                      location: 'sidebar',
+                    })
+                  }
+                }}
               >
                 <Dropdown.Toggle role="menuitem" aria-label={t('Account')}>
                   <OLTooltip

+ 14 - 1
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/admin-menu.tsx

@@ -1,6 +1,7 @@
 import type { DefaultNavbarMetadata } from '@/features/ui/components/types/default-navbar-metadata'
 import NavDropdownMenu from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-menu'
 import NavDropdownLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-link-item'
+import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
 
 export default function AdminMenu({
   canDisplayAdminMenu,
@@ -16,8 +17,20 @@ export default function AdminMenu({
   | 'canDisplaySurveyMenu'
   | 'adminUrl'
 >) {
+  const sendProjectListMB = useSendProjectListMB()
   return (
-    <NavDropdownMenu title="Admin" className="subdued">
+    <NavDropdownMenu
+      title="Admin"
+      className="subdued"
+      onToggle={nextShow => {
+        if (nextShow) {
+          sendProjectListMB('menu-expand', {
+            item: 'admin',
+            location: 'top-menu',
+          })
+        }
+      }}
+    >
       {canDisplayAdminMenu ? (
         <>
           <NavDropdownLinkItem href="/admin">Manage Site</NavDropdownLinkItem>

+ 3 - 2
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/contact-us-item.tsx

@@ -1,7 +1,7 @@
-import { sendMB } from '@/infrastructure/event-tracking'
 import { useTranslation } from 'react-i18next'
 import { DropdownItem } from 'react-bootstrap-5'
 import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
+import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
 
 export default function ContactUsItem({
   showModal,
@@ -9,6 +9,7 @@ export default function ContactUsItem({
   showModal: (event?: Event) => void
 }) {
   const { t } = useTranslation()
+  const sendMB = useSendProjectListMB()
 
   return (
     <DropdownListItem>
@@ -16,7 +17,7 @@ export default function ContactUsItem({
         as="button"
         role="menuitem"
         onClick={() => {
-          sendMB('menu-clicked-contact')
+          sendMB('menu-click', { item: 'contact', location: 'top-menu' })
           showModal()
         }}
       >

+ 1 - 5
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/default-navbar.tsx

@@ -27,7 +27,6 @@ function DefaultNavbar(props: DefaultNavbarMetadata) {
     showCloseIcon = false,
     showSubscriptionLink,
     showSignUpLink,
-    currentUrl,
     sessionUser,
     adminUrl,
     items,
@@ -125,10 +124,7 @@ function DefaultNavbar(props: DefaultNavbarMetadata) {
                       showSubscriptionLink={showSubscriptionLink}
                     />
                   ) : (
-                    <LoggedOutItems
-                      showSignUpLink={showSignUpLink}
-                      currentUrl={currentUrl}
-                    />
+                    <LoggedOutItems showSignUpLink={showSignUpLink} />
                   )}
                 </Nav>
               </Navbar.Collapse>

+ 14 - 1
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/logged-in-items.tsx

@@ -3,6 +3,7 @@ import NavDropdownMenu from '@/features/ui/components/bootstrap-5/navbar/nav-dro
 import type { NavbarSessionUser } from '@/features/ui/components/types/navbar'
 import NavLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-link-item'
 import { AccountMenuItems } from './account-menu-items'
+import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
 
 export default function LoggedInItems({
   sessionUser,
@@ -12,12 +13,24 @@ export default function LoggedInItems({
   showSubscriptionLink: boolean
 }) {
   const { t } = useTranslation()
+  const sendProjectListMB = useSendProjectListMB()
   return (
     <>
       <NavLinkItem href="/project" className="nav-item-projects">
         {t('projects')}
       </NavLinkItem>
-      <NavDropdownMenu title={t('Account')} className="nav-item-account">
+      <NavDropdownMenu
+        title={t('Account')}
+        className="nav-item-account"
+        onToggle={nextShow => {
+          if (nextShow) {
+            sendProjectListMB('menu-expand', {
+              item: 'account',
+              location: 'top-menu',
+            })
+          }
+        }}
+      >
         <AccountMenuItems
           sessionUser={sessionUser}
           showSubscriptionLink={showSubscriptionLink}

+ 6 - 7
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/logged-out-items.tsx

@@ -1,15 +1,14 @@
 import { useTranslation } from 'react-i18next'
 import NavLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-link-item'
-import { sendMB } from '@/infrastructure/event-tracking'
+import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
 
 export default function LoggedOutItems({
   showSignUpLink,
-  currentUrl,
 }: {
   showSignUpLink: boolean
-  currentUrl: string
 }) {
   const { t } = useTranslation()
+  const sendMB = useSendProjectListMB()
 
   return (
     <>
@@ -17,8 +16,8 @@ export default function LoggedOutItems({
         <NavLinkItem
           href="/register"
           className="primary nav-account-item"
-          onClick={e => {
-            sendMB('menu-clicked-register', { page: currentUrl })
+          onClick={() => {
+            sendMB('menu-click', { item: 'register', location: 'top-menu' })
           }}
         >
           {t('sign_up')}
@@ -27,8 +26,8 @@ export default function LoggedOutItems({
       <NavLinkItem
         href="/login"
         className="nav-account-item"
-        onClick={e => {
-          sendMB('menu-clicked-login', { page: currentUrl })
+        onClick={() => {
+          sendMB('menu-click', { item: 'login', location: 'top-menu' })
         }}
       >
         {t('log_in')}

+ 26 - 3
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/nav-dropdown-from-data.tsx

@@ -3,12 +3,15 @@ import type {
   NavbarItemDropdownData,
 } from '@/features/ui/components/types/navbar'
 import NavDropdownDivider from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-divider'
-import { sendMB } from '@/infrastructure/event-tracking'
 import { isDropdownLinkItem } from '@/features/ui/components/bootstrap-5/navbar/util'
 import NavDropdownLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-link-item'
 import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
 import NavDropdownMenu from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-menu'
 import ContactUsItem from '@/features/ui/components/bootstrap-5/navbar/contact-us-item'
+import {
+  type ExtraSegmentations,
+  useSendProjectListMB,
+} from '@/features/project-list/components/project-list-events'
 
 export default function NavDropdownFromData({
   item,
@@ -17,11 +20,24 @@ export default function NavDropdownFromData({
   item: NavbarDropdownItemData
   showContactUsModal: (event?: Event) => void
 }) {
+  const sendProjectListMB = useSendProjectListMB()
   return (
-    <NavDropdownMenu title={item.translatedText} className={item.class}>
+    <NavDropdownMenu
+      title={item.translatedText}
+      className={item.class}
+      onToggle={nextShow => {
+        if (nextShow) {
+          sendProjectListMB('menu-expand', {
+            item: item.trackingKey,
+            location: 'top-menu',
+          })
+        }
+      }}
+    >
       <NavDropdownMenuItems
         dropdown={item.dropdown}
         showContactUsModal={showContactUsModal}
+        location="top-menu"
       />
     </NavDropdownMenu>
   )
@@ -30,10 +46,13 @@ export default function NavDropdownFromData({
 export function NavDropdownMenuItems({
   dropdown,
   showContactUsModal,
+  location,
 }: {
   dropdown: NavbarItemDropdownData
   showContactUsModal: (event?: Event) => void
+  location: ExtraSegmentations['menu-expand']['location']
 }) {
+  const sendProjectListMB = useSendProjectListMB()
   return (
     <>
       {dropdown.map((child, index) => {
@@ -47,7 +66,11 @@ export function NavDropdownMenuItems({
               key={index}
               href={child.url}
               onClick={() => {
-                sendMB(child.event)
+                sendProjectListMB('menu-click', {
+                  item: child.trackingKey as ExtraSegmentations['menu-click']['item'],
+                  location,
+                  destinationURL: child.url,
+                })
               }}
             >
               {child.translatedText}

+ 3 - 1
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/nav-dropdown-menu.tsx

@@ -5,15 +5,17 @@ export default function NavDropdownMenu({
   title,
   className,
   children,
+  onToggle,
 }: {
   title: string
   className?: string
   children: ReactNode
+  onToggle?: (nextShow: boolean) => void
 }) {
   // Can't use a NavDropdown here because it's impossible to render the menu as
   // a <ul> element using NavDropdown
   return (
-    <Dropdown as="li" role="none" className={className}>
+    <Dropdown as="li" role="none" className={className} onToggle={onToggle}>
       <Dropdown.Toggle role="menuitem">{title}</Dropdown.Toggle>
       <Dropdown.Menu as="ul" role="menu" align="end">
         {children}

+ 7 - 2
services/web/frontend/js/features/ui/components/bootstrap-5/navbar/nav-item-from-data.tsx

@@ -5,8 +5,8 @@ import {
 } from '@/features/ui/components/bootstrap-5/navbar/util'
 import NavDropdownFromData from '@/features/ui/components/bootstrap-5/navbar/nav-dropdown-from-data'
 import NavItem from '@/features/ui/components/bootstrap-5/navbar/nav-item'
-import { sendMB } from '@/infrastructure/event-tracking'
 import NavLinkItem from '@/features/ui/components/bootstrap-5/navbar/nav-link-item'
+import { useSendProjectListMB } from '@/features/project-list/components/project-list-events'
 
 export default function NavItemFromData({
   item,
@@ -15,6 +15,7 @@ export default function NavItemFromData({
   item: NavbarItemData
   showContactUsModal: (event?: Event) => void
 }) {
+  const sendProjectListMB = useSendProjectListMB()
   if (isDropdownItem(item)) {
     return (
       <NavDropdownFromData
@@ -28,7 +29,11 @@ export default function NavItemFromData({
         className={item.class}
         href={item.url}
         onClick={() => {
-          sendMB(item.event)
+          sendProjectListMB('menu-click', {
+            item: item.trackingKey as any,
+            location: 'top-menu',
+            destinationURL: item.url,
+          })
         }}
       >
         {item.translatedText}

+ 3 - 2
services/web/frontend/js/features/ui/components/types/navbar.ts

@@ -14,7 +14,7 @@ export interface NavbarDropdownTextItem {
 
 export interface NavbarDropdownLinkItem extends NavbarDropdownTextItem {
   url: string
-  event: string
+  trackingKey: string
   eventSegmentation?: Record<string, any>
 }
 
@@ -37,11 +37,12 @@ export interface NavbarTextItemData {
 
 export interface NavbarDropdownItemData extends NavbarTextItemData {
   dropdown: NavbarItemDropdownData
+  trackingKey: 'help' | 'account' | 'features' | 'admin'
 }
 
 export interface NavbarLinkItemData extends NavbarTextItemData {
   url: string
-  event: string
+  trackingKey: string
 }
 
 export type NavbarItemData =